Browse Source

Svelto_ECS_3_0 beta first upload

pull/49/head
sebas77 4 years ago
parent
commit
99c4d40456
100 changed files with 4795 additions and 1854 deletions
  1. +1
    -1
      Svelto.Common
  2. +10
    -6
      Svelto.ECS/CheckEntityUtilities.cs
  3. +70
    -77
      Svelto.ECS/ComponentBuilder.cs
  4. +1
    -1
      Svelto.ECS/DBC.cs
  5. +74
    -0
      Svelto.ECS/DataStructures/AtomicRingBuffers.cs
  6. +298
    -0
      Svelto.ECS/DataStructures/FastTypeSafeDictionary.cs
  7. +49
    -0
      Svelto.ECS/DataStructures/ITypeSafeDictionary.cs
  8. +186
    -0
      Svelto.ECS/DataStructures/NativeBag.cs
  9. +219
    -0
      Svelto.ECS/DataStructures/NativeDynamicArray.cs
  10. +0
    -46
      Svelto.ECS/DataStructures/SetEGIDWithoutBoxing.cs
  11. +67
    -0
      Svelto.ECS/DataStructures/SharedNativeInt.cs
  12. +70
    -0
      Svelto.ECS/DataStructures/SharedNativeUInt.cs
  13. +173
    -108
      Svelto.ECS/DataStructures/TypeSafeDictionary.cs
  14. +21
    -0
      Svelto.ECS/DataStructures/TypeSafeDictionaryUtilities.cs
  15. +124
    -0
      Svelto.ECS/DataStructures/UnsafeArray.cs
  16. +267
    -0
      Svelto.ECS/DataStructures/UnsafeBlob.cs
  17. +4
    -2
      Svelto.ECS/Dispatcher/DispatchOnChange.cs
  18. +10
    -5
      Svelto.ECS/Dispatcher/DispatchOnSet.cs
  19. +32
    -32
      Svelto.ECS/DynamicEntityDescriptor.cs
  20. +2
    -2
      Svelto.ECS/ECSResources/ECSResources.cs
  21. +22
    -8
      Svelto.ECS/ECSResources/ECSString.cs
  22. +14
    -10
      Svelto.ECS/EGID.cs
  23. +7
    -0
      Svelto.ECS/EGIDComponent.cs
  24. +36
    -7
      Svelto.ECS/EGIDMapper.cs
  25. +15
    -8
      Svelto.ECS/EnginesRoot.DoubleBufferedEntityViews.cs
  26. +81
    -22
      Svelto.ECS/EnginesRoot.Engines.cs
  27. +149
    -152
      Svelto.ECS/EnginesRoot.Entities.cs
  28. +25
    -19
      Svelto.ECS/EnginesRoot.GenericEntityFactory.cs
  29. +52
    -19
      Svelto.ECS/EnginesRoot.GenericEntityFunctions.cs
  30. +38
    -44
      Svelto.ECS/EnginesRoot.Submission.cs
  31. +176
    -112
      Svelto.ECS/EntitiesDB.cs
  32. +28
    -28
      Svelto.ECS/EntityBuilder.CheckFields.cs
  33. +0
    -8
      Svelto.ECS/EntityBuilder.cs.rej
  34. +370
    -134
      Svelto.ECS/EntityCollection.cs
  35. +61
    -0
      Svelto.ECS/EntityComponentInitializer.cs
  36. +1
    -1
      Svelto.ECS/EntityDescriptorTemplate.cs
  37. +24
    -39
      Svelto.ECS/EntityFactory.cs
  38. +1
    -1
      Svelto.ECS/EntityGroupNotFoundException.cs
  39. +11
    -0
      Svelto.ECS/EntityHierarchyComponent.cs
  40. +0
    -11
      Svelto.ECS/EntityHierarchyStruct.cs
  41. +2
    -2
      Svelto.ECS/EntityInfoView.cs
  42. +91
    -48
      Svelto.ECS/EntityStream.cs
  43. +0
    -75
      Svelto.ECS/EntityStructInitializer.cs
  44. +1
    -1
      Svelto.ECS/EntitySubmissionScheduler.cs
  45. +7
    -7
      Svelto.ECS/EntitySubmitOperation.cs
  46. +45
    -51
      Svelto.ECS/EntityViewUtility.cs
  47. +38
    -120
      Svelto.ECS/ExclusiveGroup.cs
  48. +114
    -0
      Svelto.ECS/ExclusiveGroupStruct.cs
  49. +0
    -61
      Svelto.ECS/ExecuteOnEntitiesDB.cs
  50. +3
    -3
      Svelto.ECS/ExtendibleEntityDescriptor.cs
  51. +19
    -0
      Svelto.ECS/Extensions/ProcessorCount.cs
  52. +70
    -0
      Svelto.ECS/Extensions/Svelto/EntityDBExtensions.cs
  53. +117
    -0
      Svelto.ECS/Extensions/Svelto/NativeAllGroupsEnumerable.cs
  54. +221
    -0
      Svelto.ECS/Extensions/Svelto/NativeGroupsEnumerable.cs
  55. +30
    -0
      Svelto.ECS/Extensions/Unity/DOTS/CopySveltoToUECSEnginesGroup.cs
  56. +84
    -0
      Svelto.ECS/Extensions/Unity/DOTS/DisposeJob.cs
  57. +265
    -0
      Svelto.ECS/Extensions/Unity/DOTS/EnginesRoot.NativeOperation.cs
  58. +9
    -0
      Svelto.ECS/Extensions/Unity/DOTS/IJobifiedEngine.cs
  59. +31
    -0
      Svelto.ECS/Extensions/Unity/DOTS/JobifedEnginesGroup.cs
  60. +10
    -0
      Svelto.ECS/Extensions/Unity/DOTS/JobifiedSveltoEngines.cs
  61. +27
    -0
      Svelto.ECS/Extensions/Unity/DOTS/PureUECSSystemsGroup.cs
  62. +31
    -0
      Svelto.ECS/Extensions/Unity/DOTS/SortedJobifedEnginesGroup.cs
  63. +23
    -0
      Svelto.ECS/Extensions/Unity/DOTS/UECSSveltoEGID.cs
  64. +31
    -0
      Svelto.ECS/Extensions/Unity/DOTS/UnityEntityDBExtensions.cs
  65. +1
    -1
      Svelto.ECS/Extensions/Unity/GenericEntityDescriptorHolder.cs
  66. +41
    -4
      Svelto.ECS/Extensions/Unity/SveltoGUIHelper.cs
  67. +8
    -6
      Svelto.ECS/Extensions/Unity/UnityEntitySubmissionScheduler.cs
  68. +27
    -0
      Svelto.ECS/FastGroup.cs
  69. +45
    -45
      Svelto.ECS/GenericEntityDescriptor.cs
  70. +9
    -6
      Svelto.ECS/GenericentityStreamConsumerFactory.cs
  71. +60
    -0
      Svelto.ECS/GlobalTypeID.cs
  72. +112
    -0
      Svelto.ECS/GroupCompound.cs
  73. +6
    -0
      Svelto.ECS/Hybrid/IEntityViewComponent.cs
  74. +0
    -6
      Svelto.ECS/Hybrid/IEntityViewStruct.cs
  75. +1
    -1
      Svelto.ECS/Hybrid/IImplementor.cs
  76. +2
    -2
      Svelto.ECS/IComponentBuilder.cs
  77. +4
    -190
      Svelto.ECS/IEntitiesDB.cs
  78. +16
    -0
      Svelto.ECS/IEntityComponent.cs
  79. +18
    -12
      Svelto.ECS/IEntityFactory.cs
  80. +23
    -8
      Svelto.ECS/IEntityFunctions.cs
  81. +0
    -15
      Svelto.ECS/IEntityStruct.cs
  82. +1
    -1
      Svelto.ECS/IQueryingEntitiesEngine.cs
  83. +3
    -3
      Svelto.ECS/IReactOnAddAndRemove.cs
  84. +3
    -3
      Svelto.ECS/IReactOnSwap.cs
  85. +22
    -0
      Svelto.ECS/LICENSE
  86. +79
    -0
      Svelto.ECS/NativeEGIDMapper.cs
  87. +50
    -0
      Svelto.ECS/README.md
  88. +0
    -132
      Svelto.ECS/Sequencer.cs
  89. +42
    -0
      Svelto.ECS/Serialization/ComposedComponentSerializer.cs
  90. +0
    -43
      Svelto.ECS/Serialization/ComposedSerializer.cs
  91. +12
    -13
      Svelto.ECS/Serialization/DefaultSerializer.cs
  92. +19
    -3
      Svelto.ECS/Serialization/DefaultSerializerUtils.cs
  93. +22
    -15
      Svelto.ECS/Serialization/DefaultVersioningFactory.cs
  94. +1
    -1
      Svelto.ECS/Serialization/DontSerialize.cs
  95. +52
    -44
      Svelto.ECS/Serialization/EnginesRoot.GenericEntitySerialization.cs
  96. +6
    -8
      Svelto.ECS/Serialization/EnginesRoot.SerializableEntityHeader.cs
  97. +25
    -11
      Svelto.ECS/Serialization/EntitiesDB.DescriptorMap.cs
  98. +13
    -0
      Svelto.ECS/Serialization/IComponentSerializer.cs
  99. +2
    -2
      Svelto.ECS/Serialization/IDeserializationFactory.cs
  100. +12
    -8
      Svelto.ECS/Serialization/IEntitySerialization.cs

+ 1
- 1
Svelto.Common

@@ -1 +1 @@
Subproject commit 4b6f1b9f9a08cbafc9e0a46a1ad7bf30eba10e9c
Subproject commit 111fccbc4676ce1a5999570ca3c12f1918a9e763

+ 10
- 6
Svelto.ECS/CheckEntityUtilities.cs View File

@@ -1,4 +1,4 @@
#if DEBUG && !PROFILER
#if DEBUG && !PROFILE_SVELTO
using System.Collections.Generic;
using Svelto.DataStructures;
#else
@@ -7,9 +7,13 @@ using System.Diagnostics;

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.
/// </summary>
public partial class EnginesRoot
{
#if DEBUG && !PROFILER
#if DEBUG && !PROFILE_SVELTO
void CheckRemoveEntityID(EGID egid)
{
// Console.LogError("<color=orange>removed</color>".FastConcat(egid.ToString()));
@@ -48,14 +52,14 @@ namespace Svelto.ECS
.FastConcat("' id: '")
.FastConcat(egid.entityID)
.FastConcat("' groupid: '")
.FastConcat(egid.groupID)
.FastConcat(egid.groupID)
.FastConcat("'"));
}
hash.Add(egid.entityID);
}
void RemoveGroupID(ExclusiveGroup.ExclusiveGroupStruct groupID)
void RemoveGroupID(ExclusiveGroupStruct groupID)
{
_idCheckers.Remove(groupID);
}
@@ -73,7 +77,7 @@ namespace Svelto.ECS
}
[Conditional("_CHECKS_DISABLED")]
void RemoveGroupID(ExclusiveGroup.ExclusiveGroupStruct groupID)
void RemoveGroupID(ExclusiveGroupStruct groupID)
{
}
#endif


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

@@ -8,64 +8,19 @@ using Svelto.Utilities;

namespace Svelto.ECS
{
public class EntityBuilder<T> : IEntityBuilder where T : struct, IEntityStruct
public class ComponentBuilder<T> : IComponentBuilder where T : struct, IEntityComponent
{
static class EntityView
{
internal static readonly FasterList<KeyValuePair<Type, ActionCast<T>>> cachedFields;
internal static readonly Dictionary<Type, Type[]> cachedTypes;
#if DEBUG && !PROFILER
internal static readonly Dictionary<Type, ECSTuple<object, int>> implementorsByType;
#else
internal static readonly Dictionary<Type, object> implementorsByType;
#endif
static EntityView()
{
cachedFields = new FasterList<KeyValuePair<Type, ActionCast<T>>>();

var type = typeof(T);

var fields = type.GetFields(BindingFlags.Public |
BindingFlags.Instance);

for (var i = fields.Length - 1; i >= 0; --i)
{
var field = fields[i];

var setter = FastInvoke<T>.MakeSetter(field);

cachedFields.Add(new KeyValuePair<Type, ActionCast<T>>(field.FieldType, setter));
}

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

#if DEBUG && !PROFILER
implementorsByType = new Dictionary<Type, ECSTuple<object, int>>();
#else
implementorsByType = new Dictionary<Type, object>();
#endif
}

internal static void InitCache()
{}

internal static void BuildEntityView(out T entityView)
{
entityView = new T();
}
}

public EntityBuilder()
public ComponentBuilder()
{
_initializer = DEFAULT_IT;

EntityBuilderUtilities.CheckFields(ENTITY_VIEW_TYPE, NEEDS_REFLECTION);
EntityBuilderUtilities.CheckFields(ENTITY_COMPONENT_TYPE, IS_ENTITY_VIEW_COMPONENT);

if (NEEDS_REFLECTION)
EntityView.InitCache();
if (IS_ENTITY_VIEW_COMPONENT)
EntityViewComponentCache.InitCache();
}

public EntityBuilder(in T initializer) : this()
public ComponentBuilder(in T initializer) : this()
{
_initializer = initializer;
}
@@ -73,24 +28,23 @@ namespace Svelto.ECS
public void BuildEntityAndAddToList(ref ITypeSafeDictionary dictionary, EGID egid,
IEnumerable<object> implementors)
{
if (dictionary == null)
dictionary = new TypeSafeDictionary<T>();
if (dictionary == null)
dictionary = TypeSafeDictionaryFactory<T>.Create();

var castedDic = dictionary as TypeSafeDictionary<T>;
var castedDic = dictionary as ITypeSafeDictionary<T>;

if (NEEDS_REFLECTION)
T entityComponent = default;
if (IS_ENTITY_VIEW_COMPONENT)
{
DBC.ECS.Check.Require(implementors != null,
$"Implementors not found while building an EntityView `{typeof(T)}`");
$"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_VIEW_NAME}");

EntityView.BuildEntityView(out var entityView);
$"building an entity with already used entity id! id: '{(ulong) egid}', {ENTITY_COMPONENT_NAME}");

this.FillEntityView(ref entityView, entityViewBlazingFastReflection, implementors,
EntityView.implementorsByType, EntityView.cachedTypes);
this.FillEntityComponent(ref entityComponent, EntityViewComponentCache.cachedFields, implementors,
EntityViewComponentCache.implementorsByType, EntityViewComponentCache.cachedTypes);

castedDic.Add(egid.entityID, entityView);
castedDic.Add(egid.entityID, entityComponent);
}
else
{
@@ -101,7 +55,7 @@ namespace Svelto.ECS
}
}

ITypeSafeDictionary IEntityBuilder.Preallocate(ref ITypeSafeDictionary dictionary, uint size)
ITypeSafeDictionary IComponentBuilder.Preallocate(ref ITypeSafeDictionary dictionary, uint size)
{
return Preallocate(ref dictionary, size);
}
@@ -109,38 +63,77 @@ namespace Svelto.ECS
static ITypeSafeDictionary Preallocate(ref ITypeSafeDictionary dictionary, uint size)
{
if (dictionary == null)
dictionary = new TypeSafeDictionary<T>(size);
dictionary = TypeSafeDictionaryFactory<T>.Create(size);
else
dictionary.SetCapacity(size);

return dictionary;
}

public Type GetEntityType()
public Type GetEntityComponentType()
{
return ENTITY_VIEW_TYPE;
return ENTITY_COMPONENT_TYPE;
}

static EntityBuilder()
static ComponentBuilder()
{
ENTITY_VIEW_TYPE = typeof(T);
ENTITY_COMPONENT_TYPE = typeof(T);
DEFAULT_IT = default;
NEEDS_REFLECTION = typeof(IEntityViewStruct).IsAssignableFrom(ENTITY_VIEW_TYPE);
HAS_EGID = typeof(INeedEGID).IsAssignableFrom(ENTITY_VIEW_TYPE);
ENTITY_VIEW_NAME = ENTITY_VIEW_TYPE.ToString();
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
SetEGIDWithoutBoxing<T>.Warmup();
}

readonly T _initializer;

static FasterList<KeyValuePair<Type, ActionCast<T>>> entityViewBlazingFastReflection =>
EntityView.cachedFields;

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

static readonly T DEFAULT_IT;
static readonly bool NEEDS_REFLECTION;
static readonly string ENTITY_VIEW_NAME;
static readonly bool IS_ENTITY_VIEW_COMPONENT;
static readonly string ENTITY_COMPONENT_NAME;
static class EntityViewComponentCache
{
internal static readonly FasterList<KeyValuePair<Type, FastInvokeActionCast<T>>> cachedFields;
internal static readonly Dictionary<Type, Type[]> cachedTypes;
#if DEBUG && !PROFILE_SVELTO
internal static readonly Dictionary<Type, ECSTuple<object, int>> implementorsByType;
#else
internal static readonly Dictionary<Type, object> implementorsByType;
#endif
static EntityViewComponentCache()
{
cachedFields = new FasterList<KeyValuePair<Type, FastInvokeActionCast<T>>>();

var type = typeof(T);
var fields = type.GetFields(BindingFlags.Public | BindingFlags.Instance);
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);

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

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

#if DEBUG && !PROFILE_SVELTO
implementorsByType = new Dictionary<Type, ECSTuple<object, int>>();
#else
implementorsByType = new Dictionary<Type, object>();
#endif
}

internal static void InitCache()
{}
}
}
}

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

@@ -1,4 +1,4 @@
#if DISABLE_DBC || !DEBUG || PROFILER
#if DISABLE_DBC || !DEBUG || PROFILE_SVELTO
#define DISABLE_CHECKS
using System.Diagnostics;
#endif


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

@@ -0,0 +1,74 @@
using System;
using System.Runtime.CompilerServices;
using Svelto.Common;
using Allocator = Svelto.Common.Allocator;

namespace Svelto.ECS.DataStructures.Unity
{
/// <summary>
/// A collection of <see cref="NativeBag"/> intended to allow one buffer per thread.
/// from: https://github.com/jeffvella/UnityEcsEvents/blob/develop/Runtime/MultiAppendBuffer.cs
/// </summary>
public unsafe struct AtomicRingBuffers:IDisposable
{
public const int DefaultThreadIndex = -1;
public const int MinThreadIndex = DefaultThreadIndex;

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

[MethodImpl(MethodImplOptions.AggressiveInlining)]
public bool IsInvalidThreadIndex(int index) => index < MinThreadIndex || index > _threadsCount;

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

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

var ptr = (byte*)MemoryUtilities.Alloc((uint) allocationSize, (uint) MemoryUtilities.AlignOf<int>(), 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);
MemoryUtilities.CopyStructureToPtr(ref buffer, (IntPtr) bufferPtr);
}

_data = (NativeBag*)ptr;
}

[MethodImpl(MethodImplOptions.AggressiveInlining)]
public ref NativeBag GetBuffer(int index)
{
return ref MemoryUtilities.ArrayElementAsRef<NativeBag>((IntPtr) _data, index);
}

public uint count => _threadsCount;

public void Dispose()
{
for (int i = 0; i < _threadsCount; i++)
{
GetBuffer(i).Dispose();
}
MemoryUtilities.Free((IntPtr) _data, Allocator);
}

public void Clear()
{
for (int i = 0; i < _threadsCount; i++)
{
GetBuffer(i).Clear();
}
}
}
}

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

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

namespace Svelto.ECS.Internal
{
sealed class FastTypeSafeDictionary<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);

public FastTypeSafeDictionary(uint size) { _implementation = new SetDictionary<TValue>(size); }

public FastTypeSafeDictionary() { _implementation = new SetDictionary<TValue>(1); }

/// <summary>
/// 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)
{
var typeSafeDictionary = entitiesToSubmit as FastTypeSafeDictionary<TValue>;

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

_implementation.Add(tuple.Value);
}
catch (Exception e)
{
throw new
TypeSafeDictionaryException("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), e);
}
}
}

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

if (toGroup != null)
{
var toGroupCasted = toGroup as ITypeSafeDictionary<TValue>;
ref var entity = ref _implementation.unsafeValues[(int) valueIndex];

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

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

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

public void RemoveEntitiesFromEngines(
FasterDictionary<RefWrapper<Type>, FasterList<IEngine>> entityComponentEnginesDB, in PlatformProfiler profiler,
ExclusiveGroupStruct @group)
{
foreach (var value in _implementation)
RemoveEntityComponentFromEngines(entityComponentEnginesDB, ref _implementation.GetValueByRef(value.Key), null,
in profiler, new EGID(value.Key, group));
}

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

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

[MethodImpl(MethodImplOptions.AggressiveInlining)]
public void RemoveEntityFromDictionary(EGID fromEntityGid)
{
_implementation.Remove(fromEntityGid.entityID);
}

[MethodImpl(MethodImplOptions.AggressiveInlining)]
public void SetCapacity(uint size) { throw new NotImplementedException(); }

[MethodImpl(MethodImplOptions.AggressiveInlining)]
public void Trim() { _implementation.Trim(); }

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

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

ref var entity = ref _implementation.unsafeValues[(int) valueIndex];

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

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

public uint Count
{
[MethodImpl(MethodImplOptions.AggressiveInlining)]
get => _implementation.count;
}

[MethodImpl(MethodImplOptions.AggressiveInlining)]
public ITypeSafeDictionary Create() { return new FastTypeSafeDictionary<TValue>(); }

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

if (previousGroup == null)
{
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);
}
}
else
{
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);
}
}
catch (Exception e)
{
throw new
ECSException("Code crashed inside MovedTo callback ".FastConcat(typeof(TValue).ToString()),
e);
}
}
}

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

if (previousGroup == null)
{
for (var i = 0; i < entityComponentsEngines.count; i++)
try
{
using (profiler.Sample(entityComponentsEngines[i], _typeName))
(entityComponentsEngines[i] as IReactOnAddAndRemove<TValue>).Remove(ref entity, egid);
}
catch (Exception e)
{
throw new
ECSException("Code crashed inside Remove callback ".FastConcat(typeof(TValue).ToString()),
e);
}
}
#if SEEMS_UNNECESSARY
else
{
for (var i = 0; i < entityComponentsEngines.Count; i++)
try
{
using (profiler.Sample(entityComponentsEngines[i], _typeName))
(entityComponentsEngines[i] as IReactOnSwap<TValue>).MovedFrom(ref entity, egid);
}
catch (Exception e)
{
throw new ECSException(
"Code crashed inside Remove callback ".FastConcat(typeof(TValue).ToString()), e);
}
}
#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(entityComponent); }

[MethodImpl(MethodImplOptions.AggressiveInlining)]
public SetDictionary<TValue>.SetDictionaryKeyValueEnumerator GetEnumerator()
{
return _implementation.GetEnumerator();
}

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

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

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

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

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

public SetDictionary<TValue> implementation => _implementation;

[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) { throw new NotImplementedException(); }

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

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

readonly SetDictionary<TValue> _implementation;
}
}
#endif

+ 49
- 0
Svelto.ECS/DataStructures/ITypeSafeDictionary.cs View File

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

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

public interface ITypeSafeDictionary
{
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);
void Trim();
void Clear();
void FastClear();
bool Has(uint key);
bool ContainsKey(uint egidEntityId);
uint GetIndex(uint valueEntityId);
bool TryFindIndex(uint entityGidEntityId, out uint index);
}
}

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

@@ -0,0 +1,186 @@
using System;
using System.Runtime.CompilerServices;
using Svelto.Common;

namespace Svelto.ECS.DataStructures
{
/// <summary>
/// Burst friendly RingBuffer on steroid:
/// it can: Enqueue/Dequeue, it wraps if there is enough space after dequeuing
/// It resizes if there isn't enough space left.
/// It's a "bag", you can queue and dequeue any T. Just be sure that you dequeue what you queue! No check on type
/// is done.
/// You can reserve a position in the queue to update it later.
/// The datastructure is a struct and it's "copyable"
/// I eventually decided to call it NativeBag and not NativeRingBuffer 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
[global::Unity.Collections.LowLevel.Unsafe.NativeDisableUnsafePtrRestriction]
#endif
unsafe UnsafeBlob* _queue;

[MethodImpl(MethodImplOptions.AggressiveInlining)]
public bool IsEmpty()
{
unsafe
{
if (_queue == null || _queue->ptr == null)
return true;
}

return count == 0;
}

public uint count
{
[MethodImpl(MethodImplOptions.AggressiveInlining)]
get
{
unsafe
{
#if DEBUG && !PROFILE_SVELTO
if (_queue == null)
throw new Exception("SimpleNativeArray: null-access");
#endif

return _queue->size;
}
}
}

public uint capacity
{
[MethodImpl(MethodImplOptions.AggressiveInlining)]
get
{
unsafe
{
#if DEBUG && !PROFILE_SVELTO
if (_queue == null)
throw new Exception("SimpleNativeArray: null-access");
#endif

return _queue->capacity;
}
}
}

public NativeBag(Allocator allocator)
{
unsafe
{
var sizeOf = MemoryUtilities.SizeOf<UnsafeBlob>();
var listData = (UnsafeBlob*) MemoryUtilities.Alloc((uint) sizeOf
, (uint) MemoryUtilities.AlignOf<UnsafeBlob>()
, 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((uint) sizeOf
, (uint) MemoryUtilities.AlignOf<UnsafeBlob>()
, 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;
}
}

[MethodImpl(MethodImplOptions.AggressiveInlining)]
public unsafe void Dispose()
{
if (_queue != null)
{
_queue->Dispose();
_queue = null;
}
}

[MethodImpl(MethodImplOptions.AggressiveInlining)]
public ref T ReserveEnqueue<T>(out UnsafeArrayIndex index) where T : unmanaged
{
unsafe
{
#if DEBUG && !PROFILE_SVELTO
if (_queue == null)
throw new Exception("SimpleNativeArray: null-access");
#endif
var sizeOf = MemoryUtilities.SizeOf<T>();
if (_queue->space - sizeOf < 0)
_queue->Realloc((uint) MemoryUtilities.AlignOf<int>(), (uint) ((_queue->capacity + sizeOf) * 1.5f));

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

[MethodImpl(MethodImplOptions.AggressiveInlining)]
public void Enqueue<T>(in T item) where T : struct
{
unsafe
{
#if DEBUG && !PROFILE_SVELTO
if (_queue == null)
throw new Exception("SimpleNativeArray: null-access");
#endif
var sizeOf = MemoryUtilities.SizeOf<T>();
if (_queue->space - sizeOf < 0)
_queue->Realloc((uint) MemoryUtilities.AlignOf<int>(), (uint) ((_queue->capacity + sizeOf) * 1.5f));

_queue->Write(item);
}
}

[MethodImpl(MethodImplOptions.AggressiveInlining)]
public void Clear()
{
unsafe
{
#if DEBUG && !PROFILE_SVELTO
if (_queue == null)
throw new Exception("SimpleNativeArray: null-access");
#endif
_queue->Clear();
}
}

public T Dequeue<T>() where T : struct
{
unsafe
{
return _queue->Read<T>();
}
}

public ref T AccessReserved<T>(UnsafeArrayIndex reserverIndex) where T : unmanaged
{
unsafe
{
#if DEBUG && !PROFILE_SVELTO
if (_queue == null)
throw new Exception("SimpleNativeArray: null-access");
#endif
return ref _queue->AccessReserved<T>(reserverIndex);
}
}
}
}

+ 219
- 0
Svelto.ECS/DataStructures/NativeDynamicArray.cs View File

@@ -0,0 +1,219 @@
using System;
using System.Runtime.CompilerServices;
using System.Runtime.InteropServices;
using Svelto.Common;
using Allocator = Svelto.Common.Allocator;

namespace Svelto.ECS.DataStructures
{
public struct NativeDynamicArray : IDisposable
{
#if UNITY_ECS
[global::Unity.Collections.LowLevel.Unsafe.NativeDisableUnsafePtrRestriction]
#endif
unsafe UnsafeArray* _list;
#if DEBUG && !PROFILE_SVELTO
int hashType;
#endif

[MethodImpl(MethodImplOptions.AggressiveInlining)]
public uint Count<T>() where T:unmanaged
{
unsafe
{
#if DEBUG && !PROFILE_SVELTO
if (_list == null)
throw new Exception("SimpleNativeArray: null-access");
if (hashType != Svelto.Common.TypeHash<T>.hash)
throw new Exception("SimpleNativeArray: not excepted type used");

#endif
return (uint) (_list->count / MemoryUtilities.SizeOf<T>());
}
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public uint Capacity<T>() where T:unmanaged
{
unsafe
{
#if DEBUG && !PROFILE_SVELTO
if (_list == null)
throw new Exception("SimpleNativeArray: null-access");
if (hashType != Svelto.Common.TypeHash<T>.hash)
throw new Exception("SimpleNativeArray: not excepted type used");

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

public static NativeDynamicArray Alloc<T>(Allocator allocator, uint newLength = 0) where T : unmanaged
{
unsafe
{
var rtnStruc = new NativeDynamicArray();
#if DEBUG && !PROFILE_SVELTO
rtnStruc.hashType = TypeHash<T>.hash;
#endif
var sizeOf = MemoryUtilities.SizeOf<T>();
var alignOf = MemoryUtilities.AlignOf<T>();

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

listData->allocator = allocator;

listData->Realloc((uint) alignOf, (uint) (newLength * sizeOf));

rtnStruc._list = listData;

return rtnStruc;
}
}

[MethodImpl(MethodImplOptions.AggressiveInlining)]
public ref T Get<T>(uint index) where T : unmanaged
{
unsafe
{
#if DEBUG && !PROFILE_SVELTO
if (_list == null)
throw new Exception("SimpleNativeArray: null-access");
if (hashType != Svelto.Common.TypeHash<T>.hash)
throw new Exception("SimpleNativeArray: not excepted type used");
if (index >= Count<T>())
throw new Exception($"SimpleNativeArray: out of bound access, index {index} count {Count<T>()}");
#endif
return ref _list->Get<T>(index);
}
}

[MethodImpl(MethodImplOptions.AggressiveInlining)]
public void Set<T>(uint index, in T value) where T : unmanaged
{
unsafe
{
#if DEBUG && !PROFILE_SVELTO
if (_list == null)
throw new Exception("SimpleNativeArray: null-access");
if (hashType != Svelto.Common.TypeHash<T>.hash)
throw new Exception("SimpleNativeArray: not excepted type used");
if (index >= Capacity<T>())
throw new Exception($"SimpleNativeArray: out of bound access, index {index} count {Count<T>()}");
#endif
_list->Set(index, value);
}
}

public unsafe void Dispose()
{
if (_list != null)
{
_list->Dispose();
_list = null;
}
}

[MethodImpl(MethodImplOptions.AggressiveInlining)]
public void Add<T>(in T item) where T : unmanaged
{
unsafe
{
#if DEBUG && !PROFILE_SVELTO
if (_list == null)
throw new Exception("SimpleNativeArray: null-access");
if (hashType != Svelto.Common.TypeHash<T>.hash)
throw new Exception("SimpleNativeArray: not excepted type used");
#endif
var structSize = (uint) MemoryUtilities.SizeOf<T>();
if (_list->space - (int)structSize < 0)
_list->Realloc((uint) MemoryUtilities.AlignOf<T>(), (uint) ((Count<T>() + 1) * structSize * 1.5f));
//the idea is, considering the wrap, a read pointer must always be behind a writer pointer
#if DEBUG && !PROFILE_SVELTO
if (_list->space - (int)structSize < 0)
throw new Exception("no writing authorized");
#endif
_list->Add(item);
}
}

[MethodImpl(MethodImplOptions.AggressiveInlining)]
public void Clear()
{
unsafe
{
#if DEBUG && !PROFILE_SVELTO
if (_list == null)
throw new Exception("SimpleNativeArray: null-access");
#endif
_list->Clear();
}
}

public unsafe T* ToPTR<T>() where T : unmanaged
{
#if DEBUG && !PROFILE_SVELTO
if (_list == null)
throw new Exception("SimpleNativeArray: null-access");
if (hashType != Svelto.Common.TypeHash<T>.hash)
throw new Exception("SimpleNativeArray: not excepted type used");

#endif
return (T*) _list->ptr;
}

public T[] ToManagedArray<T>() where T : unmanaged
{
unsafe
{
#if DEBUG && !PROFILE_SVELTO
if (_list == null)
throw new Exception("SimpleNativeArray: null-access");
if (hashType != Svelto.Common.TypeHash<T>.hash)
throw new Exception("SimpleNativeArray: not excepted type used");

#endif
var ret = new T[Count<T>()];

var handle = GCHandle.Alloc(ret, GCHandleType.Pinned);
Buffer.MemoryCopy(_list->ptr, (void*) handle.AddrOfPinnedObject(), _list->count, _list->count);
handle.Free();

return ret;
}
}
public T[] ToManagedArrayUntrimmed<T>() where T : unmanaged
{
unsafe
{
#if DEBUG && !PROFILE_SVELTO
if (_list == null)
throw new Exception("SimpleNativeArray: null-access");
if (hashType != Svelto.Common.TypeHash<T>.hash)
throw new Exception("SimpleNativeArray: not excepted type used");

#endif
var ret = new T[Capacity<T>()];

var handle = GCHandle.Alloc(ret, GCHandleType.Pinned);
Buffer.MemoryCopy(_list->ptr, (void*) handle.AddrOfPinnedObject(), _list->capacity, _list->capacity);
handle.Free();

return ret;
}
}
}
}

+ 0
- 46
Svelto.ECS/DataStructures/SetEGIDWithoutBoxing.cs View File

@@ -1,46 +0,0 @@
using System;
using System.Linq.Expressions;
using System.Reflection;

namespace Svelto.ECS.Internal
{
static class SetEGIDWithoutBoxing<T> where T : struct, IEntityStruct
{
internal delegate void ActionCast(ref T target, EGID egid);

public static readonly ActionCast SetIDWithoutBoxing = MakeSetter();

static ActionCast MakeSetter()
{
if (EntityBuilder<T>.HAS_EGID)
{
#if !ENABLE_IL2CPP
Type myTypeA = typeof(T);
PropertyInfo myFieldInfo = myTypeA.GetProperty("ID");

ParameterExpression targetExp = Expression.Parameter(typeof(T).MakeByRefType(), "target");
ParameterExpression valueExp = Expression.Parameter(typeof(EGID), "value");
MemberExpression fieldExp = Expression.Property(targetExp, myFieldInfo);
BinaryExpression assignExp = Expression.Assign(fieldExp, valueExp);

var setter = Expression.Lambda<ActionCast>(assignExp, targetExp, valueExp).Compile();

return setter;
#else
return (ref T target, EGID value) =>
{
var needEgid = (target as INeedEGID);
needEgid.ID = value;
target = (T) needEgid;
};
#endif
}

return null;
}

public static void Warmup()
{
}
}
}

+ 67
- 0
Svelto.ECS/DataStructures/SharedNativeInt.cs View File

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

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

public static implicit operator SharedNativeInt(int t)
{
unsafe
{
var current = new SharedNativeInt();
current.data = (int*) Marshal.AllocHGlobal(sizeof(int));
*current.data = t;

return current;
}
}
public static explicit operator int(SharedNativeInt t)
{
unsafe
{
#if DEBUG && !PROFILE_SVELTO
if (t.data == null)
throw new Exception("using disposed SharedInt");
#endif

return *t.data;
}
}

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

public void Decrement()
{
unsafe
{
Interlocked.Decrement(ref *data);
}
}
public void Increment()
{
unsafe
{
Interlocked.Increment(ref *data);
}
}
}
}

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

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

+ 173
- 108
Svelto.ECS/DataStructures/TypeSafeDictionary.cs View File

@@ -1,47 +1,33 @@
using System;
using System.Runtime.CompilerServices;
using Svelto.Common;
using Svelto.DataStructures;

namespace Svelto.ECS.Internal
{
public interface ITypeSafeDictionary
{
int Count { get; }
ITypeSafeDictionary Create();

void AddEntitiesToEngines(
FasterDictionary<RefWrapper<Type>, FasterList<IEngine>> entityViewEnginesDb,
ITypeSafeDictionary realDic, in PlatformProfiler profiler, ExclusiveGroup.ExclusiveGroupStruct @group);

void RemoveEntitiesFromEngines(FasterDictionary<RefWrapper<Type>, FasterList<IEngine>> entityViewEnginesDB,
in PlatformProfiler profiler, ExclusiveGroup.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, in PlatformProfiler profiler);

void SetCapacity(uint size);
void Trim();
void Clear();
void FastClear();
bool Has(uint entityIdEntityId);
}

class TypeSafeDictionary<TValue> : FasterDictionary<uint, TValue>,
ITypeSafeDictionary where TValue : struct, IEntityStruct
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);

public TypeSafeDictionary(uint size) : base(size) {}
public TypeSafeDictionary() {}
public TypeSafeDictionary(uint size)
{
_implementation = new FasterDictionary<uint, TValue>(size);
}

public TypeSafeDictionary()
{
_implementation = new FasterDictionary<uint, TValue>(1);
}

public void AddEntitiesFromDictionary(ITypeSafeDictionary entitiesToSubmit, uint groupId)
/// <summary>
/// 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)
{
var typeSafeDictionary = entitiesToSubmit as TypeSafeDictionary<TValue>;

@@ -49,166 +35,190 @@ namespace Svelto.ECS.Internal
{
try
{
if (_hasEgid) SetEGIDWithoutBoxing<TValue>.SetIDWithoutBoxing(ref tuple.Value, new EGID(tuple.Key, groupId));
if (_hasEgid)
SetEGIDWithoutBoxing<TValue>.SetIDWithoutBoxing(ref tuple.Value, new EGID(tuple.Key, groupId));

Add(tuple.Key, tuple.Value);
_implementation.Add(tuple.Key, tuple.Value);
}
catch (Exception e)
{
throw new TypeSafeDictionaryException(
"trying to add an EntityView with the same ID more than once Entity: "
.FastConcat(typeof(TValue).ToString()).FastConcat(", group ").FastConcat(groupId).FastConcat(", id ").FastConcat(tuple.Key), e);
throw new
TypeSafeDictionaryException("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),
e);
}
}
}
public void AddEntityToDictionary(EGID fromEntityGid, EGID toEntityID, ITypeSafeDictionary toGroup)
{
var valueIndex = _implementation.GetIndex(fromEntityGid.entityID);

public void AddEntitiesToEngines(
FasterDictionary<RefWrapper<Type>, FasterList<IEngine>> entityViewEnginesDB,
ITypeSafeDictionary realDic, in PlatformProfiler profiler, ExclusiveGroup.ExclusiveGroupStruct @group)
if (toGroup != null)
{
var toGroupCasted = toGroup as ITypeSafeDictionary<TValue>;
ref var entity = ref _implementation.unsafeValues[(int) valueIndex];

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

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

public void AddEntitiesToEngines(FasterDictionary<RefWrapper<Type>, FasterList<IEngine>> entityComponentEnginesDB,
ITypeSafeDictionary realDic,
ExclusiveGroupStruct @group,
in PlatformProfiler profiler)
{
var typeSafeDictionary = realDic as TypeSafeDictionary<TValue>;
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 this)
AddEntityViewToEngines(entityViewEnginesDB, ref typeSafeDictionary.GetValueByRef(value.Key), null,
in profiler, new EGID(value.Key, group));
foreach (var value in _implementation)
AddEntityComponentToEngines(entityComponentEnginesDB, ref typeSafeDictionary.GetValueByRef(value.Key), null,
in profiler, new EGID(value.Key, group));
}

public void RemoveEntitiesFromEngines(
FasterDictionary<RefWrapper<Type>, FasterList<IEngine>> entityViewEnginesDB,
in PlatformProfiler profiler, ExclusiveGroup.ExclusiveGroupStruct @group)
FasterDictionary<RefWrapper<Type>, FasterList<IEngine>> entityComponentEnginesDB, in PlatformProfiler profiler,
ExclusiveGroupStruct @group)
{
foreach (var value in this)
RemoveEntityViewFromEngines(entityViewEnginesDB, ref GetValueByRef(value.Key), null, in profiler,
new EGID(value.Key, group));
foreach (var value in _implementation)
RemoveEntityComponentFromEngines(entityComponentEnginesDB, ref _implementation.GetValueByRef(value.Key), null,
in profiler, new EGID(value.Key, group));
}

public bool Has(uint entityIdEntityId)
{
return ContainsKey(entityIdEntityId);
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public void FastClear() { _implementation.FastClear(); }

public void RemoveEntityFromDictionary(EGID fromEntityGid, in PlatformProfiler profiler)
{
Remove(fromEntityGid.entityID);
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public bool Has(uint key) { return _implementation.ContainsKey(key); }

public void AddEntityToDictionary(EGID fromEntityGid, EGID toEntityID, ITypeSafeDictionary toGroup)
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public void RemoveEntityFromDictionary(EGID fromEntityGid)
{
var valueIndex = GetIndex(fromEntityGid.entityID);
_implementation.Remove(fromEntityGid.entityID);
}

if (toGroup != null)
{
var toGroupCasted = toGroup as TypeSafeDictionary<TValue>;
ref var entity = ref valuesArray[valueIndex];
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public void SetCapacity(uint size) { _implementation.SetCapacity(size); }

if (_hasEgid) SetEGIDWithoutBoxing<TValue>.SetIDWithoutBoxing(ref entity, toEntityID);
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public void Trim() { _implementation.Trim(); }

toGroupCasted.Add(fromEntityGid.entityID, entity);
}
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public void Clear() { _implementation.Clear(); }

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 = GetIndex(fromEntityGid.entityID);
ref var entity = ref valuesArray[valueIndex];
var valueIndex = _implementation.GetIndex(fromEntityGid.entityID);
ref var entity = ref _implementation.unsafeValues[(int) valueIndex];

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

var toGroupCasted = toGroup as TypeSafeDictionary<TValue>;
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);

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

public ITypeSafeDictionary Create()
public uint Count
{
return new TypeSafeDictionary<TValue>();
[MethodImpl(MethodImplOptions.AggressiveInlining)]
get => _implementation.count;
}

void AddEntityViewToEngines(FasterDictionary<RefWrapper<Type>, FasterList<IEngine>> entityViewEnginesDB,
ref TValue entity, ExclusiveGroup.ExclusiveGroupStruct? previousGroup,
in PlatformProfiler profiler, EGID egid)
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public ITypeSafeDictionary Create() { return new TypeSafeDictionary<TValue>(); }

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

if (previousGroup == null)
{
for (var i = 0; i < entityViewsEngines.Count; i++)
for (var i = 0; i < entityComponentsEngines.count; i++)
try
{
using (profiler.Sample(entityViewsEngines[i], _typeName))
using (profiler.Sample(entityComponentsEngines[i], _typeName))
{
(entityViewsEngines[i] as IReactOnAddAndRemove<TValue>).Add(ref entity, egid);
(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);
throw new
ECSException("Code crashed inside Add callback ".FastConcat(typeof(TValue).ToString()), e);
}
}
else
{
for (var i = 0; i < entityViewsEngines.Count; i++)
for (var i = 0; i < entityComponentsEngines.count; i++)
try
{
using (profiler.Sample(entityViewsEngines[i], _typeName))
using (profiler.Sample(entityComponentsEngines[i], _typeName))
{
(entityViewsEngines[i] as IReactOnSwap<TValue>).MovedTo(ref entity, previousGroup.Value,
egid);
(entityComponentsEngines[i] as IReactOnSwap<TValue>).MovedTo(ref entity, previousGroup.Value,
egid);
}
}
catch (Exception e)
{
throw new ECSException(
"Code crashed inside MovedTo callback ".FastConcat(typeof(TValue).ToString()), e);
throw new
ECSException("Code crashed inside MovedTo callback ".FastConcat(typeof(TValue).ToString()),
e);
}
}
}

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

if (previousGroup == null)
{
for (var i = 0; i < entityViewsEngines.Count; i++)
for (var i = 0; i < entityComponentsEngines.count; i++)
try
{
using (profiler.Sample(entityViewsEngines[i], _typeName))
(entityViewsEngines[i] as IReactOnAddAndRemove<TValue>).Remove(ref entity, egid);
using (profiler.Sample(entityComponentsEngines[i], _typeName))
(entityComponentsEngines[i] as IReactOnAddAndRemove<TValue>).Remove(ref entity, egid);
}
catch (Exception e)
{
throw new ECSException(
"Code crashed inside Remove callback ".FastConcat(typeof(TValue).ToString()), e);
throw new
ECSException("Code crashed inside Remove callback ".FastConcat(typeof(TValue).ToString()),
e);
}
}
#if SEEMS_UNNECESSARY
#if SEEMS_UNNECESSARY
else
{
for (var i = 0; i < entityViewsEngines.Count; i++)
for (var i = 0; i < entityComponentsEngines.Count; i++)
try
{
using (profiler.Sample(entityViewsEngines[i], _typeName))
(entityViewsEngines[i] as IReactOnSwap<TValue>).MovedFrom(ref entity, egid);
using (profiler.Sample(entityComponentsEngines[i], _typeName))
(entityComponentsEngines[i] as IReactOnSwap<TValue>).MovedFrom(ref entity, egid);
}
catch (Exception e)
{
@@ -218,5 +228,60 @@ namespace Svelto.ECS.Internal
}
#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()
{
return default;
}

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

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

@@ -0,0 +1,21 @@
namespace Svelto.ECS.Internal
{
static class TypeSafeDictionaryUtilities
{
internal static EGIDMapper<T> ToEGIDMapper<T>(this ITypeSafeDictionary<T> dic,
ExclusiveGroupStruct groupStructId) where T:struct, IEntityComponent
{
var mapper = new EGIDMapper<T>(groupStructId, dic);

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

+ 124
- 0
Svelto.ECS/DataStructures/UnsafeArray.cs View File

@@ -0,0 +1,124 @@
using System;
using System.Runtime.CompilerServices;
using Svelto.Common;

namespace Svelto.ECS.DataStructures
{
struct UnsafeArray : IDisposable
{
internal unsafe byte* ptr => _ptr;
//expressed in bytes
internal uint capacity { get; private set; }
//expressed in bytes
internal uint count => _writeIndex;
//expressed in bytes
internal uint space => capacity - count;

/// <summary>
/// </summary>
internal Allocator allocator;
#if DEBUG && !PROFILE_SVELTO
#pragma warning disable 649
internal uint id;
#pragma warning restore 649
#endif
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public ref T Get<T>(uint index) where T : unmanaged
{
unsafe
{
T* buffer = (T*) ptr;
return ref buffer[index];
}
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public void Set<T>(uint index, in T value) where T : unmanaged
{
unsafe
{
int sizeOf = 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;

if (_writeIndex < writeIndex + sizeOf)
_writeIndex = (uint) (writeIndex + sizeOf);
}
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public void Add<T>(in T value) where T : unmanaged
{
unsafe
{
var structSize = MemoryUtilities.SizeOf<T>();
#if DEBUG && !PROFILE_SVELTO
if (space - structSize < 0)
throw new Exception("no writing authorized");
#endif
Unsafe.Write(ptr + _writeIndex, value);

_writeIndex += (uint)structSize;
}
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
internal void Realloc(uint alignOf, uint newCapacity)
{
unsafe
{
byte* newPointer = null;
#if DEBUG && !PROFILE_SVELTO
if (capacity > 0 && newCapacity <= capacity)
throw new Exception("new capacity must be bigger than current");
#endif
if (newCapacity >= 0)
{
newPointer = (byte*) MemoryUtilities.Alloc(newCapacity, alignOf, allocator);
if (count > 0)
MemoryUtilities.MemCpy((IntPtr) newPointer, (IntPtr) ptr, count);
}

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

_ptr = newPointer;
capacity = newCapacity;
}
}

[MethodImpl(MethodImplOptions.AggressiveInlining)]
public void Dispose()
{
unsafe
{
if (ptr != null)
MemoryUtilities.Free((IntPtr) ptr, allocator);

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

[MethodImpl(MethodImplOptions.AggressiveInlining)]
public void Clear()
{
_writeIndex = 0;
}
#if UNITY_ECS
[global::Unity.Collections.LowLevel.Unsafe.NativeDisableUnsafePtrRestriction]
#endif
unsafe byte* _ptr;
uint _writeIndex;
}
}

+ 267
- 0
Svelto.ECS/DataStructures/UnsafeBlob.cs View File

@@ -0,0 +1,267 @@
using System;
using System.Runtime.CompilerServices;
using Svelto.Common;

namespace Svelto.ECS.DataStructures
{
public struct UnsafeArrayIndex
{
internal uint index;
internal uint capacity;
}

/// <summary>
/// Note: this must work inside burst, so it must follow burst restrictions
/// </summary>
struct UnsafeBlob : IDisposable
{
internal unsafe byte* ptr => _ptr;
//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 : 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 head = _writeIndex % capacity;

if (head + structSize <= capacity)
{
Unsafe.Write(ptr + head, item);
}
else
//copy with wrap, will start to copy and wrap for the reminder
{
var byteCountToEnd = capacity - head;
//need a copy to be sure that the GC won't move the data around
T copyItem = item;
void* asPointer = Unsafe.AsPointer(ref copyItem);
MemoryUtilities.MemCpy((IntPtr) (ptr + head), (IntPtr) asPointer, byteCountToEnd);
var restCount = structSize - byteCountToEnd;
//todo: check the difference between unaligned and standard
MemoryUtilities.MemCpy((IntPtr) ptr, (IntPtr) ((byte *)asPointer + byteCountToEnd), restCount);
}

uint paddedStructSize = (uint) Align4(structSize);
_writeIndex += paddedStructSize;
}
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
internal void WriteUnaligned<T>(in T item) where T : struct
{
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;
T copyItem = item;
var asPointer = Unsafe.AsPointer(ref copyItem);
Unsafe.CopyBlock(ptr + pointer, asPointer, byteCount);
var restCount = structSize - byteCount;
Unsafe.CopyBlock(ptr, (byte *)asPointer + byteCount, restCount);
}

_writeIndex += structSize;
}
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
internal T Read<T>() where T : struct
{
unsafe
{
var structSize = (uint) MemoryUtilities.SizeOf<T>();
#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 = (uint) Align4(structSize);
_readIndex += paddedStructSize;

if (_readIndex == _writeIndex)
{
//resetting the Indices has the benefit to let the Reserve work in more occasions and
//the rapping happening less often. If the _readIndex reached the _writeIndex, it means
//that there is no data left to read, so we can start to write again from the begin of the memory
_writeIndex = 0;
_readIndex = 0;
}

if (head + paddedStructSize <= capacity)
{
return Unsafe.Read<T>(ptr + head);
}
else
{
T item = default;
var byteCountToEnd = capacity - head;
var asPointer = Unsafe.AsPointer(ref item);
//todo: check the difference between unaligned and standard
MemoryUtilities.MemCpy((IntPtr) asPointer, (IntPtr) (ptr + head), byteCountToEnd);
var restCount = structSize - byteCountToEnd;
MemoryUtilities.MemCpy((IntPtr) ((byte *)asPointer + byteCountToEnd), (IntPtr) ptr, restCount);

return item;
}
}
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
internal ref T Reserve<T>(out UnsafeArrayIndex index) where T : unmanaged
{
unsafe
{
var sizeOf = (uint) MemoryUtilities.SizeOf<T>();
T* buffer = (T *)(byte*) (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
index = new UnsafeArrayIndex()
{
capacity = capacity
, index = _writeIndex
};

var align4 = (uint) Align4(sizeOf);
_writeIndex += align4;
return ref buffer[0];
}
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
internal ref T AccessReserved<T>(UnsafeArrayIndex index) where T : unmanaged
{
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*) (byte*)(ptr + index.index);
return ref buffer[0];
}
}

[MethodImpl(MethodImplOptions.AggressiveInlining)]
internal void Realloc(uint alignOf, 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);
byte* newPointer = null;
#if DEBUG && !PROFILE_SVELTO
if (newCapacity <= capacity)
throw new Exception("new capacity must be bigger than current");
#endif
if (newCapacity > 0)
{
newPointer = (byte*) MemoryUtilities.Alloc(newCapacity, alignOf, allocator);
if (size > 0)
{
var readerHead = _readIndex % capacity;
var writerHead = _writeIndex % capacity;

if (readerHead < writerHead)
{
//copy to the new pointer, from th reader position
MemoryUtilities.MemCpy((IntPtr) newPointer, (IntPtr) (ptr + readerHead), _writeIndex - _readIndex);
}
//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
//and from the start to writerHead (which is the same position of readerHead)
else
{
var byteCountToEnd = capacity - readerHead;
MemoryUtilities.MemCpy((IntPtr) newPointer, (IntPtr) (ptr + readerHead), byteCountToEnd);
MemoryUtilities.MemCpy((IntPtr) (newPointer + byteCountToEnd), (IntPtr) ptr, writerHead);
}
}
}

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

_writeIndex = size;
_readIndex = 0;
_ptr = newPointer;
capacity = newCapacity;
}
}

[MethodImpl(MethodImplOptions.AggressiveInlining)]
public void Dispose()
{
unsafe
{
if (ptr != null)
MemoryUtilities.Free((IntPtr) ptr, allocator);

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

[MethodImpl(MethodImplOptions.AggressiveInlining)]
public void Clear()
{
_writeIndex = 0;
_readIndex = 0;
}

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

+ 4
- 2
Svelto.ECS/Dispatcher/DispatchOnChange.cs View File

@@ -4,8 +4,10 @@ namespace Svelto.ECS
{
public class DispatchOnChange<T> : DispatchOnSet<T> where T:IEquatable<T>
{
public DispatchOnChange(EGID senderID) : base(senderID)
{ }
public DispatchOnChange(EGID senderID, T initialValue = default(T)) : base(senderID)
{
_value = initialValue;
}
public new T value
{


+ 10
- 5
Svelto.ECS/Dispatcher/DispatchOnSet.cs View File

@@ -16,18 +16,23 @@ namespace Svelto.ECS
_value = value;

if (_paused == false)
_subscribers(_senderID, value);
_subscriber(_senderID, value);
}
}
public void NotifyOnValueSet(Action<EGID, T> action)
{
_subscribers += action;
#if DEBUG && !PROFILE_SVELTO
DBC.ECS.Check.Require(_subscriber == null, $"{this.GetType().Name}: listener already registered");
#endif
_subscriber = action;
_paused = false;
}

public void StopNotify(Action<EGID, T> action)
public void StopNotify()
{
_subscribers -= action;
_subscriber = null;
_paused = true;
}

public void PauseNotify() { _paused = true; }
@@ -36,7 +41,7 @@ namespace Svelto.ECS
protected T _value;
readonly EGID _senderID;

Action<EGID, T> _subscribers;
Action<EGID, T> _subscriber;
bool _paused;
}
}

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

@@ -13,57 +13,57 @@ namespace Svelto.ECS
{
internal DynamicEntityDescriptor(bool isExtendible) : this()
{
var defaultEntities = EntityDescriptorTemplate<TType>.descriptor.entitiesToBuild;
var defaultEntities = EntityDescriptorTemplate<TType>.descriptor.componentsToBuild;
var length = defaultEntities.Length;

_entitiesToBuild = new IEntityBuilder[length + 1];
ComponentsToBuild = new IComponentBuilder[length + 1];

Array.Copy(defaultEntities, 0, _entitiesToBuild, 0, length);
Array.Copy(defaultEntities, 0, ComponentsToBuild, 0, length);

//assign it after otherwise the previous copy will overwrite the value in case the item
//is already present
_entitiesToBuild[length] = new EntityBuilder<EntityStructInfoView>
ComponentsToBuild[length] = new ComponentBuilder<EntityInfoComponentView>
(
new EntityStructInfoView
new EntityInfoComponentView
{
entitiesToBuild = _entitiesToBuild
componentsToBuild = ComponentsToBuild
}
);
}

public DynamicEntityDescriptor(IEntityBuilder[] extraEntityBuilders) : this()
public DynamicEntityDescriptor(IComponentBuilder[] extraEntityBuilders) : this()
{
var extraEntitiesLength = extraEntityBuilders.Length;

_entitiesToBuild = Construct(extraEntitiesLength, extraEntityBuilders,
EntityDescriptorTemplate<TType>.descriptor.entitiesToBuild);
ComponentsToBuild = Construct(extraEntitiesLength, extraEntityBuilders,
EntityDescriptorTemplate<TType>.descriptor.componentsToBuild);
}

public DynamicEntityDescriptor(FasterList<IEntityBuilder> extraEntityBuilders) : this()
public DynamicEntityDescriptor(FasterList<IComponentBuilder> extraEntityBuilders) : this()
{
var extraEntities = extraEntityBuilders.ToArrayFast();
var extraEntitiesLength = extraEntityBuilders.Count;
var extraEntities = extraEntityBuilders.ToArrayFast(out _);
var extraEntitiesLength = extraEntityBuilders.count;

_entitiesToBuild = Construct(extraEntitiesLength, extraEntities,
EntityDescriptorTemplate<TType>.descriptor.entitiesToBuild);
ComponentsToBuild = Construct((int) extraEntitiesLength, extraEntities,
EntityDescriptorTemplate<TType>.descriptor.componentsToBuild);
}

public void ExtendWith<T>() where T : IEntityDescriptor, new()
{
var newEntitiesToBuild = EntityDescriptorTemplate<T>.descriptor.entitiesToBuild;
var newEntitiesToBuild = EntityDescriptorTemplate<T>.descriptor.componentsToBuild;

_entitiesToBuild = Construct(newEntitiesToBuild.Length, newEntitiesToBuild, _entitiesToBuild);
ComponentsToBuild = Construct(newEntitiesToBuild.Length, newEntitiesToBuild, ComponentsToBuild);
}
public void ExtendWith(IEntityBuilder[] extraEntities)
public void ExtendWith(IComponentBuilder[] extraEntities)
{
_entitiesToBuild = Construct(extraEntities.Length, extraEntities, _entitiesToBuild);
ComponentsToBuild = Construct(extraEntities.Length, extraEntities, ComponentsToBuild);
}

static IEntityBuilder[] Construct(int extraEntitiesLength, IEntityBuilder[] extraEntities,
IEntityBuilder[] startingEntities)
static IComponentBuilder[] Construct(int extraEntitiesLength, IComponentBuilder[] extraEntities,
IComponentBuilder[] startingEntities)
{
IEntityBuilder[] localEntitiesToBuild;
IComponentBuilder[] localEntitiesToBuild;

if (extraEntitiesLength == 0)
{
@@ -74,24 +74,24 @@ namespace Svelto.ECS
var defaultEntities = startingEntities;
var length = defaultEntities.Length;

var index = SetupSpecialEntityStruct(defaultEntities, out localEntitiesToBuild, extraEntitiesLength);
var index = SetupSpecialEntityComponent(defaultEntities, out localEntitiesToBuild, extraEntitiesLength);

Array.Copy(extraEntities, 0, localEntitiesToBuild, length, extraEntitiesLength);

//assign it after otherwise the previous copy will overwrite the value in case the item
//is already present
localEntitiesToBuild[index] = new EntityBuilder<EntityStructInfoView>
localEntitiesToBuild[index] = new ComponentBuilder<EntityInfoComponentView>
(
new EntityStructInfoView
new EntityInfoComponentView
{
entitiesToBuild = localEntitiesToBuild
componentsToBuild = localEntitiesToBuild
}
);

return localEntitiesToBuild;
}

static int SetupSpecialEntityStruct(IEntityBuilder[] defaultEntities, out IEntityBuilder[] entitiesToBuild,
static int SetupSpecialEntityComponent(IComponentBuilder[] defaultEntities, out IComponentBuilder[] componentsToBuild,
int extraLenght)
{
int length = defaultEntities.Length;
@@ -100,7 +100,7 @@ namespace Svelto.ECS
for (var i = 0; i < length; i++)
{
//the special entity already exists
if (defaultEntities[i].GetEntityType() == EntityBuilderUtilities.ENTITY_STRUCT_INFO_VIEW)
if (defaultEntities[i].GetEntityComponentType() == EntityBuilderUtilities.ENTITY_STRUCT_INFO_VIEW)
{
index = i;
break;
@@ -110,19 +110,19 @@ namespace Svelto.ECS
if (index == -1)
{
index = length + extraLenght;
entitiesToBuild = new IEntityBuilder[index + 1];
componentsToBuild = new IComponentBuilder[index + 1];
}
else
entitiesToBuild = new IEntityBuilder[length + extraLenght];
componentsToBuild = new IComponentBuilder[length + extraLenght];

Array.Copy(defaultEntities, 0, entitiesToBuild, 0, length);
Array.Copy(defaultEntities, 0, componentsToBuild, 0, length);

return index;
}


public IEntityBuilder[] entitiesToBuild => _entitiesToBuild;
public IComponentBuilder[] componentsToBuild => ComponentsToBuild;

IEntityBuilder[] _entitiesToBuild;
IComponentBuilder[] ComponentsToBuild;
}
}

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

@@ -22,12 +22,12 @@ namespace Svelto.ECS.Experimental
{
_resources.Add(resource);

return (uint)_resources.Count;
return (uint)_resources.count;
}

public static T FromECS(uint id)
{
if (id - 1 < _resources.Count)
if (id - 1 < _resources.count)
return _resources[(int) id - 1];
return default;


+ 22
- 8
Svelto.ECS/ECSResources/ECSString.cs View File

@@ -5,34 +5,48 @@ namespace Svelto.ECS.Experimental
[Serialization.DoNotSerialize]
public struct ECSString:IEquatable<ECSString>
{
uint id;
uint _id;

public ECSString(string newText)
{
id = ResourcesECSDB<string>.ToECS(newText);
_id = ResourcesECSDB<string>.ToECS(newText);
}
ECSString(uint id)
{
_id = id;
}

public static implicit operator string(ECSString ecsString)
{
return ResourcesECSDB<string>.FromECS(ecsString.id);
return ResourcesECSDB<string>.FromECS(ecsString._id);
}
public void Set(string newText)
{
if (id != 0)
ResourcesECSDB<string>.resources(id) = newText;
if (_id != 0)
ResourcesECSDB<string>.resources(_id) = newText;
else
id = ResourcesECSDB<string>.ToECS(newText);
_id = ResourcesECSDB<string>.ToECS(newText);
}

public ECSString Copy()
{
DBC.ECS.Check.Require(_id != 0, "copying not initialized string");
var id = ResourcesECSDB<string>.ToECS(ResourcesECSDB<string>.resources(_id));
return new ECSString(id);
}

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

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

+ 14
- 10
Svelto.ECS/EGID.cs View File

@@ -1,5 +1,5 @@
using System;
using System.Collections.Generic;
using System.Runtime.InteropServices;

#pragma warning disable 660,661

@@ -8,12 +8,13 @@ namespace Svelto.ECS
//todo: add debug map
[Serialization.DoNotSerialize]
[Serializable]
public struct EGID:IEquatable<EGID>,IEqualityComparer<EGID>,IComparable<EGID>
[StructLayout(LayoutKind.Explicit)]
public struct EGID:IEquatable<EGID>,IComparable<EGID>
{
public uint entityID => (uint) (_GID & 0xFFFFFFFF);
public ExclusiveGroup.ExclusiveGroupStruct groupID => new ExclusiveGroup.ExclusiveGroupStruct((uint) (_GID >> 32));
[FieldOffset(0)] public readonly uint entityID;
[FieldOffset(4)] public readonly ExclusiveGroupStruct groupID;
[FieldOffset(0)] readonly ulong _GID;
public static bool operator ==(EGID obj1, EGID obj2)
{
return obj1._GID == obj2._GID;
@@ -24,7 +25,7 @@ namespace Svelto.ECS
return obj1._GID != obj2._GID;
}

public EGID(uint entityID, ExclusiveGroup.ExclusiveGroupStruct groupID) : this()
public EGID(uint entityID, ExclusiveGroupStruct groupID) : this()
{
_GID = MAKE_GLOBAL_ID(entityID, groupID);
}
@@ -52,11 +53,16 @@ namespace Svelto.ECS
return x == y;
}

public int GetHashCode(EGID obj)
public override int GetHashCode()
{
return _GID.GetHashCode();
}

public int GetHashCode(EGID egid)
{
return egid.GetHashCode();
}

public int CompareTo(EGID other)
{
return _GID.CompareTo(other._GID);
@@ -71,7 +77,5 @@ namespace Svelto.ECS
{
return "id ".FastConcat(entityID).FastConcat(" group ").FastConcat(groupID);
}

readonly ulong _GID;
}
}

+ 7
- 0
Svelto.ECS/EGIDComponent.cs View File

@@ -0,0 +1,7 @@
namespace Svelto.ECS
{
public struct EGIDComponent:IEntityComponent, INeedEGID
{
public EGID ID { get; set; }
}
}

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

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

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

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

[MethodImpl(MethodImplOptions.AggressiveInlining)]
public ref T Entity(uint entityID)
{
#if DEBUG && !PROFILER
#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.valuesArray[findIndex];
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);
value = map.unsafeValues[index];
return true;
}

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

throw new ECSException("Entity not found");
}
public bool TryGetArrayAndEntityIndex(uint entityID, out uint index, out T[] array)
{
if (map.TryFindIndex(entityID, out index))
{
array = map.unsafeValues;
return true;
}

array = default;
return false;
}
}
}


+ 15
- 8
Svelto.ECS/EnginesRoot.DoubleBufferedEntityViews.cs View File

@@ -26,18 +26,18 @@ namespace Svelto.ECS
public void ClearOther()
{
//do not clear the groups created so far, they will be reused, unless they are too many!
var otherCount = other.Count;
var otherCount = other.count;
if (otherCount > MaximumNumberOfItemsPerFrameBeforeToClear)
{
otherEntitiesCreatedPerGroup.FastClear();
other.FastClear();
return;
}
var otherValuesArray = other.valuesArray;
var otherValuesArray = other.unsafeValues;
for (int i = 0; i < otherCount; ++i)
{
var safeDictionariesCount = otherValuesArray[i].Count;
var safeDictionaries = otherValuesArray[i].valuesArray;
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)
{
@@ -56,18 +56,25 @@ namespace Svelto.ECS
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>>
_entityViewsToAddBufferA =
_entityComponentsToAddBufferA =
new FasterDictionary<uint, FasterDictionary<RefWrapper<Type>, ITypeSafeDictionary>>();

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

readonly FasterDictionary<uint, uint> _entitiesCreatedPerGroupA = new FasterDictionary<uint, uint>();
@@ -78,8 +85,8 @@ namespace Svelto.ECS
currentEntitiesCreatedPerGroup = _entitiesCreatedPerGroupA;
otherEntitiesCreatedPerGroup = _entitiesCreatedPerGroupB;

current = _entityViewsToAddBufferA;
other = _entityViewsToAddBufferB;
current = _entityComponentsToAddBufferA;
other = _entityComponentsToAddBufferB;
}
}
}

+ 81
- 22
Svelto.ECS/EnginesRoot.Engines.cs View File

@@ -1,5 +1,6 @@
using System;
using System.Collections.Generic;
using Svelto.Common;
using Svelto.DataStructures;
using Svelto.ECS.Internal;
using Svelto.ECS.Schedulers;
@@ -12,17 +13,20 @@ namespace Svelto.ECS
{
public EntitiesSubmitter(EnginesRoot enginesRoot)
{
_weakReference = new DataStructures.WeakReference<EnginesRoot>(enginesRoot);
_weakReference = new Svelto.DataStructures.WeakReference<EnginesRoot>(enginesRoot);
}

public void Invoke()
{
if (_weakReference.IsValid)
_weakReference.Target.SubmitEntityViews();
_weakReference.Target.SubmitEntityComponents();
}

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

/// <summary>
/// Engines root contextualize your engines and entities. You don't need to limit yourself to one EngineRoot
/// as multiple engines root could promote separation of scopes. The EntitySubmissionScheduler checks
@@ -31,9 +35,9 @@ namespace Svelto.ECS
/// The EntitySubmissionScheduler cannot hold an EnginesRoot reference, that's why
/// it must receive a weak reference of the EnginesRoot callback.
/// </summary>
public EnginesRoot(IEntitySubmissionScheduler entityViewScheduler)
public EnginesRoot(IEntitiesSubmissionScheduler entitiesComponentScheduler)
{
_entitiesOperations = new FasterDictionary<ulong, EntitySubmitOperation>();
_entitiesOperations = new ThreadSafeDictionary<ulong, EntitySubmitOperation>();
serializationDescriptorMap = new SerializationDescriptorMap();
_reactiveEnginesAddRemove = new FasterDictionary<RefWrapper<Type>, FasterList<IEngine>>();
_reactiveEnginesSwap = new FasterDictionary<RefWrapper<Type>, FasterList<IEngine>>();
@@ -42,26 +46,82 @@ namespace Svelto.ECS
_disposableEngines = new FasterList<IDisposable>();
_transientEntitiesOperations = new FasterList<EntitySubmitOperation>();

_groupEntityViewsDB = new FasterDictionary<uint, FasterDictionary<RefWrapper<Type>, 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(_groupEntityViewsDB, _groupsPerEntity, _entitiesStream);
_entitiesDB = new EntitiesDB(_groupEntityComponentsDB, _groupsPerEntity, _entitiesStream);

_scheduler = entityViewScheduler;
_scheduler.onTick = new EntitiesSubmitter(this);
scheduler = entitiesComponentScheduler;
scheduler.onTick = new EntitiesSubmitter(this);
#if UNITY_ECS
AllocateNativeOperations();
#endif
}
public EnginesRoot(IEntitySubmissionScheduler entityViewScheduler, bool isDeserializationOnly):this(entityViewScheduler)
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.
/// It's a clean up process.
/// </summary>
public void Dispose()
{
using (var profiler = new PlatformProfiler("Final Dispose"))
{
foreach (var groups in _groupEntityComponentsDB)
{
foreach (var entityList in groups.Value)
{
entityList.Value.RemoveEntitiesFromEngines(_reactiveEnginesAddRemove, profiler,
new ExclusiveGroupStruct(groups.Key));
}
}

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

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

_disposableEngines.Clear();
_enginesSet.Clear();
_enginesTypeSet.Clear();
_reactiveEnginesSwap.Clear();
_reactiveEnginesAddRemove.Clear();

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

_entitiesStream.Dispose();
}

GC.SuppressFinalize(this);
}

~EnginesRoot()
{
Console.LogWarning("Engines Root has been garbage collected, don't forget to call Dispose()!");

Dispose();
}


public void AddEngine(IEngine engine)
{
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,
@@ -70,10 +130,10 @@ namespace Svelto.ECS
try
{
if (engine is IReactOnAddAndRemove viewEngine)
CheckEntityViewsEngine(viewEngine, _reactiveEnginesAddRemove);
CheckEntityComponentsEngine(viewEngine, _reactiveEnginesAddRemove);

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

_enginesTypeSet.Add(refWrapper);
_enginesSet.Add(engine);
@@ -81,10 +141,10 @@ namespace Svelto.ECS
if (engine is IDisposable)
_disposableEngines.Add(engine as IDisposable);

if (engine is IQueryingEntitiesEngine queryableEntityViewEngine)
if (engine is IQueryingEntitiesEngine queryableEntityComponentEngine)
{
queryableEntityViewEngine.entitiesDB = _entitiesDB;
queryableEntityViewEngine.Ready();
queryableEntityComponentEngine.entitiesDB = _entitiesDB;
queryableEntityComponentEngine.Ready();
}
}
catch (Exception e)
@@ -93,7 +153,7 @@ namespace Svelto.ECS
}
}

void CheckEntityViewsEngine<T>(T engine, FasterDictionary<RefWrapper<Type>, FasterList<IEngine>> engines)
void CheckEntityComponentsEngine<T>(T engine, FasterDictionary<RefWrapper<Type>, FasterList<IEngine>> engines)
where T : class, IEngine
{
var interfaces = engine.GetType().GetInterfaces();
@@ -109,13 +169,13 @@ namespace Svelto.ECS
}
}

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

AddEngine(engine, engines, type);
}
@@ -137,8 +197,7 @@ namespace Svelto.ECS
readonly FasterDictionary<RefWrapper<Type>, FasterList<IEngine>> _reactiveEnginesAddRemove;
readonly FasterDictionary<RefWrapper<Type>, FasterList<IEngine>> _reactiveEnginesSwap;
readonly FasterList<IDisposable> _disposableEngines;
readonly FasterList<IEngine> _enginesSet;
readonly HashSet<Type> _enginesTypeSet;
readonly FasterList<IEngine> _enginesSet;
readonly HashSet<Type> _enginesTypeSet;
}
}

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

@@ -4,62 +4,12 @@ using System.Runtime.CompilerServices;
using Svelto.Common;
using Svelto.DataStructures;
using Svelto.ECS.Internal;
using Svelto.ECS.Schedulers;

namespace Svelto.ECS
{
public partial class EnginesRoot : IDisposable
{
/// <summary>
/// Dispose an EngineRoot once not used anymore, so that all the
/// engines are notified with the entities removed.
/// It's a clean up process.
/// </summary>
public void Dispose()
{
using (var profiler = new PlatformProfiler("Final Dispose"))
{
foreach (var groups in _groupEntityViewsDB)
{
foreach (var entityList in groups.Value)
{
entityList.Value.RemoveEntitiesFromEngines(_reactiveEnginesAddRemove,
profiler, new ExclusiveGroup.ExclusiveGroupStruct(groups.Key));
}
}

_groupEntityViewsDB.Clear();
_groupsPerEntity.Clear();

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

_disposableEngines.Clear();
_enginesSet.Clear();
_enginesTypeSet.Clear();
_reactiveEnginesSwap.Clear();
_reactiveEnginesAddRemove.Clear();

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

_entitiesStream.Dispose();
}

GC.SuppressFinalize(this);
}

~EnginesRoot()
{
Console.LogWarning("Engines Root has been garbage collected, don't forget to call Dispose()!");

Dispose();
}

///--------------------------------------------
///
public IEntityStreamConsumerFactory GenerateConsumerFactory()
@@ -79,41 +29,37 @@ namespace Svelto.ECS

///--------------------------------------------
[MethodImpl(MethodImplOptions.AggressiveInlining)]
EntityStructInitializer BuildEntity(EGID entityID, IEntityBuilder[] entitiesToBuild,
EntityComponentInitializer BuildEntity(EGID entityID, IComponentBuilder[] componentsToBuild,
IEnumerable<object> implementors = null)
{
CheckAddEntityID(entityID);

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

return new EntityStructInitializer(entityID, dic);
return new EntityComponentInitializer(entityID, dic);
}

///--------------------------------------------
void Preallocate<T>(uint groupID, uint size) where T : IEntityDescriptor, new()
void Preallocate<T>(ExclusiveGroupStruct groupID, uint size) where T : IEntityDescriptor, new()
{
var entityViewsToBuild = EntityDescriptorTemplate<T>.descriptor.entitiesToBuild;
var numberOfEntityViews = entityViewsToBuild.Length;
var entityComponentsToBuild = EntityDescriptorTemplate<T>.descriptor.componentsToBuild;
var numberOfEntityComponents = entityComponentsToBuild.Length;

//reserve space in the database
if (_groupEntityViewsDB.TryGetValue(groupID, out var group) == false)
group = _groupEntityViewsDB[groupID] = new FasterDictionary<RefWrapper<Type>, ITypeSafeDictionary>();
FasterDictionary<RefWrapper<Type>, ITypeSafeDictionary> group = GetOrCreateGroup(groupID);

for (var index = 0; index < numberOfEntityViews; index++)
for (var index = 0; index < numberOfEntityComponents; index++)
{
var entityViewBuilder = entityViewsToBuild[index];
var entityViewType = entityViewBuilder.GetEntityType();
var entityComponentBuilder = entityComponentsToBuild[index];
var entityComponentType = entityComponentBuilder.GetEntityComponentType();

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

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

groupedGroup[groupID] = dbList;
}
@@ -121,29 +67,27 @@ namespace Svelto.ECS

///--------------------------------------------
///
void MoveEntityFromAndToEngines(IEntityBuilder[] entityBuilders, EGID fromEntityGID, EGID? toEntityGID)
void MoveEntityFromAndToEngines(IComponentBuilder[] entityBuilders, EGID fromEntityGID, EGID? toEntityGID)
{
using (var sampler = new PlatformProfiler("Move Entity From Engines"))
{
//for each entity view generated by the entity descriptor
if (_groupEntityViewsDB.TryGetValue(fromEntityGID.groupID, out var fromGroup) == false)
throw new ECSException("from group not found eid: ".FastConcat(fromEntityGID.entityID)
.FastConcat(" group: ").FastConcat(fromEntityGID.groupID));
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 TypeSafeDictionary<EntityStructInfoView>).TryGetValue(
fromEntityGID.entityID, out var entityInfoView))
MoveEntities(fromEntityGID, toEntityGID, entityInfoView.entitiesToBuild, fromGroup, sampler);
(entityInfoViewDic as ITypeSafeDictionary<EntityInfoComponentView>).TryGetValue(fromEntityGID.entityID,
out var entityInfoView))
MoveEntityComponents(fromEntityGID, toEntityGID, entityInfoView.componentsToBuild, fromGroup, sampler);
//otherwise it's a normal static entity descriptor
else
MoveEntities(fromEntityGID, toEntityGID, entityBuilders, fromGroup, sampler);
MoveEntityComponents(fromEntityGID, toEntityGID, entityBuilders, fromGroup, sampler);
}
}

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

@@ -151,136 +95,188 @@ namespace Svelto.ECS
{
var toGroupID = toEntityGID.Value.groupID;

if (_groupEntityViewsDB.TryGetValue(toGroupID, out toGroup) == false)
toGroup = _groupEntityViewsDB[toGroupID] = new FasterDictionary<RefWrapper<Type>, ITypeSafeDictionary>();
toGroup = GetOrCreateGroup(toGroupID);

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

//call all the callbacks
for (var i = 0; i < entitiesToMove.Length; i++)
MoveEntityViewFromAndToEngines(fromEntityGID, toEntityGID, fromGroup, toGroup,
entitiesToMove[i].GetEntityType(), sampler);
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].GetEntityType(), sampler);
for (var i = 0; i < entitiesToMove.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 entityViewType)
FasterDictionary<RefWrapper<Type>, ITypeSafeDictionary> toGroup,
Type entityComponentType)
{
var wrapper = new RefWrapper<Type>(entityViewType);
var wrapper = new RefWrapper<Type>(entityComponentType);

if (fromGroup.TryGetValue(wrapper, out var fromTypeSafeDictionary) == false)
{
throw new ECSException("no entities in from group eid: ".FastConcat(entityGID.entityID)
.FastConcat(" group: ").FastConcat(entityGID.groupID));
}
ITypeSafeDictionary fromTypeSafeDictionary = GetTypeSafeDictionary(entityGID.groupID, fromGroup, wrapper);

#if DEBUG && !PROFILER
#if DEBUG && !PROFILE_SVELTO
if (fromTypeSafeDictionary.Has(entityGID.entityID) == false)
{
throw new EntityNotFoundException(entityGID, entityViewType);
throw new EntityNotFoundException(entityGID, entityComponentType);
}
#endif
if (toGroup.TryGetValue(wrapper, out var toEntitiesDictionary) == false)
{
toEntitiesDictionary = fromTypeSafeDictionary.Create();
toGroup.Add(wrapper, toEntitiesDictionary);
}

//todo: this must be unit tested properly
if (_groupsPerEntity.TryGetValue(wrapper, out var groupedGroup) == false)
groupedGroup = _groupsPerEntity[wrapper] =
new FasterDictionary<uint, ITypeSafeDictionary>();

groupedGroup[toEntityGID.groupID] = toEntitiesDictionary;
ITypeSafeDictionary toEntitiesDictionary =
GetOrCreateTypeSafeDictionary(toEntityGID.groupID, toGroup, wrapper, fromTypeSafeDictionary);

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

void MoveEntityViewFromAndToEngines(EGID entityGID, EGID? toEntityGID,
void MoveEntityComponentFromAndToEngines(EGID entityGID, EGID? toEntityGID,
FasterDictionary<RefWrapper<Type>, ITypeSafeDictionary> fromGroup,
FasterDictionary<RefWrapper<Type>, ITypeSafeDictionary> toGroup, Type entityViewType,
in PlatformProfiler profiler)
FasterDictionary<RefWrapper<Type>, ITypeSafeDictionary> toGroup,
Type entityComponentType, in PlatformProfiler profiler)
{
//add all the entities
var refWrapper = new RefWrapper<Type>(entityViewType);
if (fromGroup.TryGetValue(refWrapper, out var fromTypeSafeDictionary) == false)
{
throw new ECSException("no entities in from group eid: ".FastConcat(entityGID.entityID)
.FastConcat(" group: ").FastConcat(entityGID.groupID));
}
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

#if DEBUG && !PROFILER
#if DEBUG && !PROFILE_SVELTO
if (fromTypeSafeDictionary.Has(entityGID.entityID) == false)
throw new EntityNotFoundException(entityGID, entityViewType);
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 entityViewType,
in PlatformProfiler profiler)
FasterDictionary<RefWrapper<Type>, ITypeSafeDictionary> fromGroup,
Type entityComponentType, in PlatformProfiler profiler)
{
var refWrapper = new RefWrapper<Type>(entityViewType);
if (fromGroup.TryGetValue(refWrapper, out var fromTypeSafeDictionary) == false)
{
throw new ECSException("no entities in from group eid: ".FastConcat(entityGID.entityID)
.FastConcat(" group: ").FastConcat(entityGID.groupID));
}
var refWrapper = new RefWrapper<Type>(entityComponentType);
var fromTypeSafeDictionary = GetTypeSafeDictionary(entityGID.groupID, fromGroup, refWrapper);

fromTypeSafeDictionary.RemoveEntityFromDictionary(entityGID, profiler);
fromTypeSafeDictionary.RemoveEntityFromDictionary(entityGID);

if (fromTypeSafeDictionary.Count == 0) //clean up
//if (fromTypeSafeDictionary.Count == 0) //clean up
{
//todo: this must be unit tested properly
_groupsPerEntity[refWrapper].Remove(entityGID.groupID);
//_groupsPerEntity[refWrapper].Remove(entityGID.groupID);
//I don't remove the group if empty on purpose, in case it needs to be reused
}
}

/// <summary>
/// Todo: I should keep the group, but I need to mark the group as deleted for the Exist function to work
/// Swap all the entities from one group to another
/// </summary>
/// <param name="groupID"></param>
/// <param name="fromIdGroupId"></param>
/// <param name="toGroupId"></param>
/// <param name="profiler"></param>
void RemoveGroupAndEntitiesFromDB(uint groupID, in PlatformProfiler profiler)
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)
{
//call all the MoveTo callbacks
dictionaryOfEntities.Value.AddEntitiesToEngines(_reactiveEnginesAddRemove, dictionaryOfEntities.Value,
new ExclusiveGroupStruct(toGroupId), profiler);

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

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

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

typeSafeDictionary.FastClear();
}
}

FasterDictionary<RefWrapper<Type>, ITypeSafeDictionary> GetGroup(uint fromIdGroupId)
{
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)
{
var dictionariesOfEntities = _groupEntityViewsDB[groupID];
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)
{
//be sure that the TypeSafeDictionary for the entity Type exists
if (toGroup.TryGetValue(type, out ITypeSafeDictionary toEntitiesDictionary) ==
false)
{
toEntitiesDictionary = fromTypeSafeDictionary.Create();
toGroup.Add(type, toEntitiesDictionary);
}

//update GroupsPerEntity
if (_groupsPerEntity.TryGetValue(type, out var groupedGroup) == false)
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)
{
if (@group.TryGetValue(refWrapper, out ITypeSafeDictionary fromTypeSafeDictionary) == false)
{
throw new ECSException("no group found: ".FastConcat(groupID));
}

return fromTypeSafeDictionary;
}

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

foreach (var dictionaryOfEntities in dictionariesOfEntities)
{
dictionaryOfEntities.Value.RemoveEntitiesFromEngines(_reactiveEnginesAddRemove, profiler,
new ExclusiveGroup.ExclusiveGroupStruct(groupID));
var groupedGroupOfEntities = _groupsPerEntity[dictionaryOfEntities.Key];
groupedGroupOfEntities.Remove(groupID);
}
new ExclusiveGroupStruct(groupID));
dictionaryOfEntities.Value.FastClear();

//careful, in this case I assume you really don't want to use this group anymore
//so I remove it from the database
_groupEntityViewsDB.Remove(groupID);
FasterDictionary<uint, ITypeSafeDictionary> groupsOfEntityType =
_groupsPerEntity[dictionaryOfEntities.Key];
groupsOfEntityType[groupID].FastClear();
}
}

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


public Consumer<T> GenerateConsumer<T>(ExclusiveGroup group, string name, uint capacity) where T : unmanaged,
IEntityStruct
internal Consumer<T> GenerateConsumer<T>(ExclusiveGroup group, string name, uint capacity)
where T : unmanaged, IEntityComponent
{
return _entitiesStream.GenerateConsumer<T>(group, name, capacity);
}
@@ -288,15 +284,16 @@ namespace Svelto.ECS
//one datastructure rule them all:
//split by group
//split by type per group. It's possible to get all the entities of a give type T per group thanks
//to the FasterDictionary capabilities OR it's possible to get a specific entityView indexed by
//to the FasterDictionary capabilities OR it's possible to get a specific entityComponent indexed by
//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
//ITypeSafeDictionary = Key = entityID, Value = EntityStruct
readonly FasterDictionary<uint, FasterDictionary<RefWrapper<Type>, ITypeSafeDictionary>> _groupEntityViewsDB;
// group EntityComponentType entityID, EntityComponent
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
//EntityViewType //groupID //entityID, EntityStruct
//found indexed by group id. TypeSafeDictionary are never created, they instead point to the ones hold
//by _groupEntityComponentsDB
// EntityComponentType groupID entityID, EntityComponent
readonly FasterDictionary<RefWrapper<Type>, FasterDictionary<uint, ITypeSafeDictionary>> _groupsPerEntity;

readonly EntitiesDB _entitiesDB;


+ 25
- 19
Svelto.ECS/EnginesRoot.GenericEntityFactory.cs View File

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

namespace Svelto.ECS
{
@@ -12,45 +13,50 @@ namespace Svelto.ECS
_enginesRoot = new WeakReference<EnginesRoot>(weakReference);
}

public EntityStructInitializer BuildEntity<T>(uint entityID,
ExclusiveGroup.ExclusiveGroupStruct groupStructId, IEnumerable<object> implementors = null)
public EntityComponentInitializer BuildEntity<T>
(uint entityID, ExclusiveGroupStruct groupStructId, IEnumerable<object> implementors = null)
where T : IEntityDescriptor, new()
{
return _enginesRoot.Target.BuildEntity(new EGID(entityID, groupStructId),
EntityDescriptorTemplate<T>.descriptor.entitiesToBuild, implementors);
return _enginesRoot.Target.BuildEntity(new EGID(entityID, groupStructId)
, EntityDescriptorTemplate<T>.descriptor.componentsToBuild
, implementors);
}

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

public EntityStructInitializer BuildEntity<T>(EGID egid, T entityDescriptor,
IEnumerable<object> implementors)
where T : IEntityDescriptor
public EntityComponentInitializer BuildEntity<T>
(EGID egid, T entityDescriptor, IEnumerable<object> implementors) where T : IEntityDescriptor
{
return _enginesRoot.Target.BuildEntity(egid, entityDescriptor.entitiesToBuild, implementors);
return _enginesRoot.Target.BuildEntity(egid, entityDescriptor.componentsToBuild, implementors);
}

public EntityStructInitializer BuildEntity<T>(uint entityID,
ExclusiveGroup.ExclusiveGroupStruct groupStructId, T descriptorEntity, IEnumerable<object> implementors)
#if UNITY_ECS
public NativeEntityFactory ToNative<T>(Unity.Collections.Allocator allocator) where T : IEntityDescriptor, new()
{
return _enginesRoot.Target.ProvideNativeEntityFactoryQueue<T>();
}
#endif
public EntityComponentInitializer BuildEntity<T>
(uint entityID, ExclusiveGroupStruct groupStructId, T descriptorEntity, IEnumerable<object> implementors)
where T : IEntityDescriptor
{
return _enginesRoot.Target.BuildEntity(new EGID(entityID, groupStructId),
descriptorEntity.entitiesToBuild,
implementors);
return _enginesRoot.Target.BuildEntity(new EGID(entityID, groupStructId)
, descriptorEntity.componentsToBuild, implementors);
}

public void PreallocateEntitySpace<T>(ExclusiveGroup.ExclusiveGroupStruct groupStructId, uint size)
public void PreallocateEntitySpace<T>(ExclusiveGroupStruct groupStructId, uint size)
where T : IEntityDescriptor, new()
{
_enginesRoot.Target.Preallocate<T>(groupStructId, size);
}

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

+ 52
- 19
Svelto.ECS/EnginesRoot.GenericEntityFunctions.cs View File

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

namespace Svelto.ECS
{
@@ -10,15 +10,15 @@ namespace Svelto.ECS
/// todo: EnginesRoot was a weakreference to give the change to inject
/// entityfunctions from other engines root. It probably should be reverted
/// </summary>
sealed class GenericEntityFunctions : IEntityFunctions
class GenericEntityFunctions : IEntityFunctions
{
internal GenericEntityFunctions(EnginesRoot weakReference)
{
_enginesRoot = new WeakReference<EnginesRoot>(weakReference);
_enginesRoot = new Svelto.DataStructures.WeakReference<EnginesRoot>(weakReference);
}

[MethodImpl(MethodImplOptions.AggressiveInlining)]
public void RemoveEntity<T>(uint entityID, ExclusiveGroup.ExclusiveGroupStruct groupID) where T :
public void RemoveEntity<T>(uint entityID, ExclusiveGroupStruct groupID) where T :
IEntityDescriptor, new()
{
RemoveEntity<T>(new EGID(entityID, groupID));
@@ -31,11 +31,23 @@ namespace Svelto.ECS

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

[MethodImpl(MethodImplOptions.AggressiveInlining)]
public void RemoveGroupAndEntities(ExclusiveGroup.ExclusiveGroupStruct groupID)
public void RemoveGroupAndEntities(ExclusiveGroupStruct groupID)
{
_enginesRoot.Target.RemoveGroupID(groupID);

@@ -44,23 +56,32 @@ namespace Svelto.ECS
}

[MethodImpl(MethodImplOptions.AggressiveInlining)]
public void SwapEntityGroup<T>(uint entityID, ExclusiveGroup.ExclusiveGroupStruct fromGroupID,
ExclusiveGroup.ExclusiveGroupStruct toGroupID)
public void SwapEntitiesInGroup<T>(ExclusiveGroupStruct fromGroupID, ExclusiveGroupStruct toGroupID)
{
throw new NotImplementedException("can't run this until I add the checks!");
_enginesRoot.Target.QueueEntitySubmitOperation(
new EntitySubmitOperation(EntitySubmitOperationType.SwapGroup, new EGID(0, fromGroupID),
new EGID(0, toGroupID)));
}

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

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

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

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

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

[MethodImpl(MethodImplOptions.AggressiveInlining)]
public void SwapEntityGroup<T>(EGID fromID, EGID toID)
where T : IEntityDescriptor, new()
@@ -89,17 +122,17 @@ namespace Svelto.ECS

_enginesRoot.Target.QueueEntitySubmitOperation<T>(
new EntitySubmitOperation(EntitySubmitOperationType.Swap,
fromID, toID, EntityDescriptorTemplate<T>.descriptor.entitiesToBuild));
fromID, toID, EntityDescriptorTemplate<T>.descriptor.componentsToBuild));
}
//enginesRoot is a weakreference because GenericEntityStreamConsumerFactory can be injected inside
//engines of other enginesRoot
readonly WeakReference<EnginesRoot> _enginesRoot;
readonly Svelto.DataStructures.WeakReference<EnginesRoot> _enginesRoot;
}

void QueueEntitySubmitOperation(EntitySubmitOperation entitySubmitOperation)
{
#if DEBUG && !PROFILER
#if DEBUG && !PROFILE_SVELTO
entitySubmitOperation.trace = new StackFrame(1, true);
#endif
_entitiesOperations.Add((ulong) entitySubmitOperation.fromID, entitySubmitOperation);
@@ -107,14 +140,14 @@ namespace Svelto.ECS

void QueueEntitySubmitOperation<T>(EntitySubmitOperation entitySubmitOperation) where T : IEntityDescriptor
{
#if DEBUG && !PROFILER
#if DEBUG && !PROFILE_SVELTO
entitySubmitOperation.trace = new StackFrame(1, true);

if (_entitiesOperations.TryGetValue((ulong) entitySubmitOperation.fromID, out var entitySubmitedOperation))
{
if (entitySubmitedOperation != entitySubmitOperation)
throw new ECSException("Only one entity operation per submission is allowed"
.FastConcat(" entityViewType: ")
.FastConcat(" entityComponentType: ")
.FastConcat(typeof(T).Name)
.FastConcat(" submission type ", entitySubmitOperation.type.ToString(),
" from ID: ", entitySubmitOperation.fromID.entityID.ToString())


+ 38
- 44
Svelto.ECS/EnginesRoot.Submission.cs View File

@@ -2,7 +2,6 @@
using Svelto.Common;
using Svelto.DataStructures;
using Svelto.ECS.Internal;
using Svelto.ECS.Schedulers;

namespace Svelto.ECS
{
@@ -10,7 +9,7 @@ namespace Svelto.ECS
{
readonly FasterList<EntitySubmitOperation> _transientEntitiesOperations;

void SubmitEntityViews()
void SubmitEntityComponents()
{
using (var profiler = new PlatformProfiler("Svelto.ECS - Entities Submission"))
{
@@ -18,10 +17,10 @@ namespace Svelto.ECS
do
{
SingleSubmission(profiler);
} while ((_groupedEntityToAdd.currentEntitiesCreatedPerGroup.Count > 0 ||
} while ((_groupedEntityToAdd.currentEntitiesCreatedPerGroup.count > 0 ||
_entitiesOperations.Count > 0) && ++iterations < 5);

#if DEBUG && !PROFILER
#if DEBUG && !PROFILE_SVELTO
if (iterations == 5)
throw new ECSException("possible circular submission detected");
#endif
@@ -30,17 +29,20 @@ namespace Svelto.ECS

void SingleSubmission(in PlatformProfiler profiler)
{
#if UNITY_ECS
NativeOperationSubmission(profiler);
#endif
if (_entitiesOperations.Count > 0)
{
using (profiler.Sample("Remove and Swap operations"))
{
_transientEntitiesOperations.FastClear();
var entitySubmitOperations = _entitiesOperations.GetValuesArray(out var count);
_transientEntitiesOperations.AddRange(entitySubmitOperations, count);
_entitiesOperations.CopyValuesTo(_transientEntitiesOperations);
_entitiesOperations.FastClear();

var entitiesOperations = _transientEntitiesOperations.ToArrayFast();
for (var i = 0; i < _transientEntitiesOperations.Count; i++)
EntitySubmitOperation[] entitiesOperations = _transientEntitiesOperations.ToArrayFast(out var count);
for (var i = 0; i < count; i++)
{
try
{
@@ -48,17 +50,20 @@ namespace Svelto.ECS
{
case EntitySubmitOperationType.Swap:
MoveEntityFromAndToEngines(entitiesOperations[i].builders,
entitiesOperations[i].fromID,
entitiesOperations[i].toID);
entitiesOperations[i].fromID, entitiesOperations[i].toID);
break;
case EntitySubmitOperationType.Remove:
MoveEntityFromAndToEngines(entitiesOperations[i].builders,
entitiesOperations[i].fromID, null);
break;
case EntitySubmitOperationType.RemoveGroup:
RemoveGroupAndEntitiesFromDB(
RemoveGroupAndEntities(
entitiesOperations[i].fromID.groupID, profiler);
break;
case EntitySubmitOperationType.SwapGroup:
SwapEntitiesBetweenGroups(entitiesOperations[i].fromID.groupID,
entitiesOperations[i].toID.groupID, profiler);
break;
}
}
catch (Exception e)
@@ -67,7 +72,7 @@ namespace Svelto.ECS
.FastConcat(entitiesOperations[i].type.ToString());

throw new ECSException(str.FastConcat(" ")
#if DEBUG && !PROFILER
#if DEBUG && !PROFILE_SVELTO
.FastConcat(entitiesOperations[i].trace.ToString())
#endif
, e);
@@ -78,17 +83,17 @@ namespace Svelto.ECS

_groupedEntityToAdd.Swap();

if (_groupedEntityToAdd.otherEntitiesCreatedPerGroup.Count > 0)
if (_groupedEntityToAdd.otherEntitiesCreatedPerGroup.count > 0)
{
using (profiler.Sample("Add operations"))
{
try
{
AddEntityViewsToTheDBAndSuitableEngines(profiler);
AddEntityComponentsToTheDBAndSuitableEngines(profiler);
}
finally
{
using (profiler.Sample("clear operates double buffering"))
using (profiler.Sample("clear 6operates double buffering"))
{
//other can be cleared now, but let's avoid deleting the dictionary every time
_groupedEntityToAdd.ClearOther();
@@ -98,7 +103,7 @@ namespace Svelto.ECS
}
}

void AddEntityViewsToTheDBAndSuitableEngines(in PlatformProfiler profiler)
void AddEntityComponentsToTheDBAndSuitableEngines(in PlatformProfiler profiler)
{
using (profiler.Sample("Add entities to database"))
{
@@ -106,30 +111,21 @@ namespace Svelto.ECS
foreach (var groupOfEntitiesToSubmit in _groupedEntityToAdd.otherEntitiesCreatedPerGroup)
{
var groupID = groupOfEntitiesToSubmit.Key;
FasterDictionary<RefWrapper<Type>, ITypeSafeDictionary> groupDB = GetOrCreateGroup(groupID);

//if the group doesn't exist in the current DB let's create it first
if (_groupEntityViewsDB.TryGetValue(groupID, out var groupDB) == false)
groupDB = _groupEntityViewsDB[groupID] =
new FasterDictionary<RefWrapper<Type>, ITypeSafeDictionary>();

//add the entityViews in the group
foreach (var entityViewsToSubmit in _groupedEntityToAdd.other[groupID])
//add the entityComponents in the group
foreach (var entityComponentsToSubmit in _groupedEntityToAdd.other[groupID])
{
var type = entityViewsToSubmit.Key;
var typeSafeDictionary = entityViewsToSubmit.Value;

var type = entityComponentsToSubmit.Key;
var targetTypeSafeDictionary = entityComponentsToSubmit.Value;
var wrapper = new RefWrapper<Type>(type);
if (groupDB.TryGetValue(wrapper, out var dbDic) == false)
dbDic = groupDB[wrapper] = typeSafeDictionary.Create();

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

if (_groupsPerEntity.TryGetValue(wrapper, out var groupedGroup) == false)
groupedGroup = _groupsPerEntity[wrapper] =
new FasterDictionary<uint, ITypeSafeDictionary>();
ITypeSafeDictionary dbDic = GetOrCreateTypeSafeDictionary(groupID, groupDB, wrapper,
targetTypeSafeDictionary);

groupedGroup[groupID] = dbDic;
//Fill the DB with the entity views generate this frame.
dbDic.AddEntitiesFromDictionary(targetTypeSafeDictionary, groupID);
}
}
}
@@ -141,22 +137,20 @@ namespace Svelto.ECS
foreach (var groupToSubmit in _groupedEntityToAdd.otherEntitiesCreatedPerGroup)
{
var groupID = groupToSubmit.Key;
var groupDB = _groupEntityComponentsDB[groupID];

var groupDB = _groupEntityViewsDB[groupID];

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

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

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

+ 176
- 112
Svelto.ECS/EntitiesDB.cs View File

@@ -1,164 +1,191 @@
#if DEBUG && !PROFILER
#if DEBUG && !PROFILE_SVELTO
#define ENABLE_DEBUG_FUNC
#endif

using System;
using System.Runtime.CompilerServices;
using Svelto.Common;
using Svelto.DataStructures;
using Svelto.ECS.Internal;

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

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

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

[MethodImpl(MethodImplOptions.AggressiveInlining)]
public ref T QueryEntity<T>(EGID entityGID) where T : struct, IEntityStruct
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[index];
return ref array[(int) index];

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

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

[MethodImpl(MethodImplOptions.AggressiveInlining)]
public T[] QueryEntities<T>(ExclusiveGroup.ExclusiveGroupStruct groupStruct, out uint count)
where T : struct, IEntityStruct
/// <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
/// regardless the number of entities built.
/// </summary>
/// <param name="groupStructId"></param>
/// <typeparam name="T"></typeparam>
/// <returns></returns>
public EntityCollection<T> QueryEntities<T>(ExclusiveGroupStruct groupStructId)
where T : struct, IEntityComponent
{
uint group = groupStruct;
count = 0;
if (SafeQueryEntityDictionary(group, out TypeSafeDictionary<T> typeSafeDictionary) == false)
return RetrieveEmptyEntityViewArray<T>();
T[] ret;
uint count = 0;
//object sentinel = default;
if (SafeQueryEntityDictionary<T>(groupStructId, out var typeSafeDictionary) == false)
ret = RetrieveEmptyEntityComponentArray<T>();
else
{
var safeDictionary = (typeSafeDictionary as ITypeSafeDictionary<T>);
ret = safeDictionary.GetValuesArray(out count);
// sentinel = safeDictionary.GenerateSentinel();
}

return typeSafeDictionary.GetValuesArray(out count);
return new EntityCollection<T>(ret, count);
}

public EntityCollection<T> QueryEntities<T>(ExclusiveGroup.ExclusiveGroupStruct groupStruct)
where T : struct, IEntityStruct
public EntityCollection<T1, T2> QueryEntities<T1, T2>(
ExclusiveGroupStruct groupStruct)
where T1 : struct, IEntityComponent where T2 : struct, IEntityComponent
{
return new EntityCollection<T>(QueryEntities<T>(groupStruct, out var count), count);
}
var T1entities = QueryEntities<T1>(groupStruct);
var T2entities = QueryEntities<T2>(groupStruct);

public EntityCollection<T1, T2> QueryEntities<T1, T2>(ExclusiveGroup.ExclusiveGroupStruct groupStruct)
where T1 : struct, IEntityStruct where T2 : struct, IEntityStruct
{
return new EntityCollection<T1, T2>(QueryEntities<T1, T2>(groupStruct, out var count), count);
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("'")));

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

public EntityCollection<T1, T2, T3> QueryEntities<T1, T2, T3>(ExclusiveGroup.ExclusiveGroupStruct groupStruct)
where T1 : struct, IEntityStruct where T2 : struct, IEntityStruct where T3 : struct, IEntityStruct
public EntityCollection<T1, T2, T3>
QueryEntities<T1, T2, T3>(ExclusiveGroupStruct groupStruct)
where T1 : struct, IEntityComponent where T2 : struct, IEntityComponent where T3 : struct, IEntityComponent
{
return new EntityCollection<T1, T2, T3>(QueryEntities<T1, T2, T3>(groupStruct, out var count), count);
var T1entities = QueryEntities<T1>(groupStruct);
var T2entities = QueryEntities<T2>(groupStruct);
var T3entities = QueryEntities<T3>(groupStruct);

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

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

public EntityCollections<T1, T2> QueryEntities<T1, T2>(ExclusiveGroup[] groups)
where T1 : struct, IEntityStruct where T2 : struct, IEntityStruct
where T1 : struct, IEntityComponent where T2 : struct, IEntityComponent
{
return new EntityCollections<T1, T2>(this, groups);
}
public EntityCollections<T1, T2, T3> QueryEntities<T1, T2, T3>(ExclusiveGroup[] groups)
where T1 : struct, IEntityComponent where T2 : struct, IEntityComponent where T3 : struct, IEntityComponent
{
return new EntityCollections<T1, T2, T3>(this, groups);
}

[MethodImpl(MethodImplOptions.AggressiveInlining)]
public (T1[], T2[]) QueryEntities<T1, T2>(ExclusiveGroup.ExclusiveGroupStruct groupStruct, out uint count)
where T1 : struct, IEntityStruct
where T2 : struct, IEntityStruct
public EGIDMapper<T> QueryMappedEntities<T>(ExclusiveGroupStruct groupStructId)
where T : struct, IEntityComponent
{
var T1entities = QueryEntities<T1>(groupStruct, out var countCheck);
var T2entities = QueryEntities<T2>(groupStruct, out count);

if (count != countCheck)
{
throw new ECSException("Entity views count do not match in group. Entity 1: ' count: "
.FastConcat(countCheck)
.FastConcat(typeof(T1).ToString())
.FastConcat("'. Entity 2: ' count: ".FastConcat(count)
.FastConcat(typeof(T2).ToString())
.FastConcat("'")));
}
if (SafeQueryEntityDictionary<T>(groupStructId, out var typeSafeDictionary) == false)
throw new EntityGroupNotFoundException(typeof(T));

return (T1entities, T2entities);
return (typeSafeDictionary as ITypeSafeDictionary<T>).ToEGIDMapper(groupStructId);
}

[MethodImpl(MethodImplOptions.AggressiveInlining)]
public (T1[], T2[], T3[]) QueryEntities
<T1, T2, T3>(ExclusiveGroup.ExclusiveGroupStruct groupStruct, out uint count)
where T1 : struct, IEntityStruct where T2 : struct, IEntityStruct where T3 : struct, IEntityStruct
public NativeEGIDMapper<T> QueryNativeMappedEntities<T>(ExclusiveGroupStruct groupStructId)
where T : unmanaged, IEntityComponent
{
var T1entities = QueryEntities<T1>(groupStruct, out var countCheck1);
var T2entities = QueryEntities<T2>(groupStruct, out var countCheck2);
var T3entities = QueryEntities<T3>(groupStruct, out count);

if (count != countCheck1 || count != countCheck2)
throw new ECSException("Entity views count do not match in group. Entity 1: "
.FastConcat(typeof(T1).ToString()).FastConcat(" count: ").FastConcat(countCheck1).FastConcat(
" Entity 2: ".FastConcat(typeof(T2).ToString())
.FastConcat(" count: ").FastConcat(countCheck2)
.FastConcat(" Entity 3: ".FastConcat(typeof(T3).ToString())).FastConcat(" count: ")
.FastConcat(count)));
if (SafeQueryEntityDictionary<T>(groupStructId, out var typeSafeDictionary) == false)
throw new EntityGroupNotFoundException(typeof(T));

return (T1entities, T2entities, T3entities);
return (typeSafeDictionary as TypeSafeDictionary<T>).ToNativeEGIDMapper<T>(groupStructId);
}

[MethodImpl(MethodImplOptions.AggressiveInlining)]
public EGIDMapper<T> QueryMappedEntities<T>(ExclusiveGroup.ExclusiveGroupStruct groupStructId)
where T : struct, IEntityStruct
public bool TryQueryMappedEntities<T>(ExclusiveGroupStruct groupStructId,
out EGIDMapper<T> mapper)
where T : struct, IEntityComponent
{
if (SafeQueryEntityDictionary(groupStructId, out TypeSafeDictionary<T> typeSafeDictionary) == false)
throw new EntityGroupNotFoundException(groupStructId, typeof(T));
mapper = default;
if (SafeQueryEntityDictionary<T>(groupStructId, out var typeSafeDictionary) == false ||
typeSafeDictionary.Count == 0)
return false;

EGIDMapper<T> mapper;
mapper.map = typeSafeDictionary;
mapper = (typeSafeDictionary as ITypeSafeDictionary<T>).ToEGIDMapper(groupStructId);

return mapper;
return true;
}

[MethodImpl(MethodImplOptions.AggressiveInlining)]
public bool TryQueryMappedEntities<T>(ExclusiveGroup.ExclusiveGroupStruct groupStructId,
out EGIDMapper<T> mapper)
where T : struct, IEntityStruct
public bool TryQueryNativeMappedEntities<T>(ExclusiveGroupStruct groupStructId,
out NativeEGIDMapper<T> mapper)
where T : unmanaged, IEntityComponent
{
mapper = default;
if (SafeQueryEntityDictionary(groupStructId, out TypeSafeDictionary<T> typeSafeDictionary) == false)
if (SafeQueryEntityDictionary<T>(groupStructId, out var typeSafeDictionary) == false ||
typeSafeDictionary.Count == 0)
return false;

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

return true;
}

[MethodImpl(MethodImplOptions.AggressiveInlining)]
public T[] QueryEntitiesAndIndex<T>(EGID entityGID, out uint index) where T : struct, IEntityStruct
public T[] QueryEntitiesAndIndex<T>(EGID entityGID, out uint index) where T : struct, IEntityComponent
{
T[] array;
if ((array = QueryEntitiesAndIndexInternal<T>(entityGID, out index)) != null)
@@ -169,7 +196,7 @@ namespace Svelto.ECS.Internal

[MethodImpl(MethodImplOptions.AggressiveInlining)]
public bool TryQueryEntitiesAndIndex<T>(EGID entityGid, out uint index, out T[] array)
where T : struct, IEntityStruct
where T : struct, IEntityComponent
{
if ((array = QueryEntitiesAndIndexInternal<T>(entityGid, out index)) != null)
return true;
@@ -178,77 +205,108 @@ namespace Svelto.ECS.Internal
}

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

[MethodImpl(MethodImplOptions.AggressiveInlining)]
public bool TryQueryEntitiesAndIndex<T>(uint id, ExclusiveGroup.ExclusiveGroupStruct group, out uint index,
out T[] array) where T : struct, IEntityStruct
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);
return TryQueryEntitiesAndIndex(new EGID(id, @group), out index, out array);
}

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

return casted != null && casted.ContainsKey(entityGID.entityID);
}

[MethodImpl(MethodImplOptions.AggressiveInlining)]
public bool Exists<T>(uint id, ExclusiveGroup.ExclusiveGroupStruct group) where T : struct, IEntityStruct
public bool Exists<T>(uint id, ExclusiveGroupStruct group) where T : struct, IEntityComponent
{
if (SafeQueryEntityDictionary(group, out TypeSafeDictionary<T> casted) == false) return false;
if (SafeQueryEntityDictionary<T>(group, out var casted) == false) return false;

return casted != null && casted.ContainsKey(id);
}

[MethodImpl(MethodImplOptions.AggressiveInlining)]
public bool Exists(ExclusiveGroup.ExclusiveGroupStruct gid)
public bool ExistsAndIsNotEmpty(ExclusiveGroupStruct gid)
{
return _groupEntityViewsDB.ContainsKey(gid);
if (_groupEntityComponentsDB.TryGetValue(gid,
out FasterDictionary<RefWrapper<Type>, ITypeSafeDictionary> group) == true)
{
return group.count > 0;
}

return false;
}

[MethodImpl(MethodImplOptions.AggressiveInlining)]
public bool HasAny<T>(ExclusiveGroup.ExclusiveGroupStruct groupStruct) where T : struct, IEntityStruct
public bool HasAny<T>(ExclusiveGroupStruct groupStruct) where T : struct, IEntityComponent
{
QueryEntities<T>(groupStruct, out var count);
return count > 0;
return QueryEntities<T>(groupStruct).count > 0;
}

[MethodImpl(MethodImplOptions.AggressiveInlining)]
public uint Count<T>(ExclusiveGroup.ExclusiveGroupStruct groupStruct) where T : struct, IEntityStruct
public uint Count<T>(ExclusiveGroupStruct groupStruct) where T : struct, IEntityComponent
{
QueryEntities<T>(groupStruct, out var count);
return count;
return QueryEntities<T>(groupStruct).count;
}

[MethodImpl(MethodImplOptions.AggressiveInlining)]
public void PublishEntityChange<T>(EGID egid) where T : unmanaged, IEntityStruct
public void PublishEntityChange<T>(EGID egid) where T : unmanaged, IEntityComponent
{
_entityStream.PublishEntity(ref QueryEntity<T>(egid), egid);
}

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

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

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

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

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

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

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

[MethodImpl(MethodImplOptions.AggressiveInlining)]
bool SafeQueryEntityDictionary<T>(uint group, out TypeSafeDictionary<T> typeSafeDictionary)
where T : struct, IEntityStruct
bool SafeQueryEntityDictionary<T>(uint group, out ITypeSafeDictionary typeSafeDictionary)
where T : struct, IEntityComponent
{
if (UnsafeQueryEntityDictionary(group, TypeCache<T>.type, out var safeDictionary) == false)
{
@@ -257,7 +315,7 @@ namespace Svelto.ECS.Internal
}

//return the indexes entities if they exist
typeSafeDictionary = safeDictionary as TypeSafeDictionary<T>;
typeSafeDictionary = safeDictionary;

return true;
}
@@ -266,7 +324,7 @@ namespace Svelto.ECS.Internal
internal bool UnsafeQueryEntityDictionary(uint group, Type type, out ITypeSafeDictionary typeSafeDictionary)
{
//search for the group
if (_groupEntityViewsDB.TryGetValue(group, out var entitiesInGroupPerType) == false)
if (_groupEntityComponentsDB.TryGetValue(group, out var entitiesInGroupPerType) == false)
{
typeSafeDictionary = null;
return false;
@@ -277,25 +335,31 @@ namespace Svelto.ECS.Internal
}

[MethodImpl(MethodImplOptions.AggressiveInlining)]
static T[] RetrieveEmptyEntityViewArray<T>()
static T[] RetrieveEmptyEntityComponentArray<T>()
{
return EmptyList<T>.emptyArray;
}

static class EmptyList<T>
{
internal static readonly T[] emptyArray = new T[0];
}
internal FasterDictionary<uint, ITypeSafeDictionary> FindGroups<T1>() where T1 : unmanaged, IEntityComponent
{
return _groupsPerEntity[TypeRefWrapper<T1>.wrapper];
}
readonly EntitiesStream _entityStream;

//grouped set of entity views, this is the standard way to handle entity views entity views 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
//a specific type inside a specific group.
readonly FasterDictionary<uint, FasterDictionary<RefWrapper<Type>, ITypeSafeDictionary>> _groupEntityViewsDB;
readonly FasterDictionary<uint, FasterDictionary<RefWrapper<Type>, ITypeSafeDictionary>> _groupEntityComponentsDB;

//needed to be able to iterate over all the entities of the same type regardless the group
//may change in future
//needed to be able to track in which groups a specific entity type can be found.
//may change in future as it could be expanded to support queries
readonly FasterDictionary<RefWrapper<Type>, FasterDictionary<uint, ITypeSafeDictionary>> _groupsPerEntity;
readonly EntitiesStream _entityStream;

static class EmptyList<T>
{
internal static readonly T[] emptyArray = new T[0];
}
}
}

+ 28
- 28
Svelto.ECS/EntityBuilder.CheckFields.cs View File

@@ -1,4 +1,4 @@
#if !DEBUG || PROFILER
#if !DEBUG || PROFILE_SVELTO
#define DISABLE_CHECKS
using System.Diagnostics;
#endif
@@ -15,40 +15,40 @@ namespace Svelto.ECS
#if DISABLE_CHECKS
[Conditional("_CHECKS_DISABLED")]
#endif
public static void CheckFields(Type entityStructType, bool needsReflection, bool isStringAllowed = false)
public static void CheckFields(Type entityComponentType, bool needsReflection, bool isStringAllowed = false)
{
if (entityStructType == ENTITY_STRUCT_INFO_VIEW ||
entityStructType == EGIDType ||
entityStructType == EXCLUSIVEGROUPSTRUCTTYPE ||
entityStructType == SERIALIZABLE_ENTITY_STRUCT)
if (entityComponentType == ENTITY_STRUCT_INFO_VIEW ||
entityComponentType == EGIDType ||
entityComponentType == EXCLUSIVEGROUPSTRUCTTYPE ||
entityComponentType == SERIALIZABLE_ENTITY_STRUCT)
{
return;
}

if (needsReflection == false)
{
if (entityStructType.IsClass)
if (entityComponentType.IsClass)
{
throw new EntityStructException("EntityStructs must be structs.", entityStructType);
throw new EntityComponentException("EntityComponents must be structs.", entityComponentType);
}

FieldInfo[] fields = entityStructType.GetFields(BindingFlags.Public | BindingFlags.Instance);
FieldInfo[] fields = entityComponentType.GetFields(BindingFlags.Public | BindingFlags.Instance);

for (var i = fields.Length - 1; i >= 0; --i)
{
FieldInfo fieldInfo = fields[i];
Type fieldType = fieldInfo.FieldType;

SubCheckFields(fieldType, entityStructType, isStringAllowed);
SubCheckFields(fieldType, entityComponentType, isStringAllowed);
}
}
else
{
FieldInfo[] fields = entityStructType.GetFields(BindingFlags.Public | BindingFlags.Instance);
FieldInfo[] fields = entityComponentType.GetFields(BindingFlags.Public | BindingFlags.Instance);

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

for (int i = fields.Length - 1; i >= 0; --i)
@@ -58,7 +58,7 @@ namespace Svelto.ECS
if (fieldInfo.FieldType.IsInterfaceEx() == false)
{
ProcessError("Entity View Structs must hold only entity components interfaces.",
entityStructType);
entityComponentType);
}

PropertyInfo[] properties = fieldInfo.FieldType.GetProperties(
@@ -79,16 +79,16 @@ namespace Svelto.ECS
Type propertyType = properties[j].PropertyType;
if (propertyType != STRINGTYPE)
{
//for EntityViewStructs, component fields that are structs that hold strings
//for EntityComponentStructs, component fields that are structs that hold strings
//are allowed
SubCheckFields(propertyType, entityStructType, isStringAllowed: true);
SubCheckFields(propertyType, entityComponentType, isStringAllowed: true);
}
}
}
}
}

static void SubCheckFields(Type fieldType, Type entityStructType, bool isStringAllowed = false)
static void SubCheckFields(Type fieldType, Type entityComponentType, bool isStringAllowed = false)
{
if (fieldType.IsPrimitive || fieldType.IsValueType || (isStringAllowed == true && fieldType == STRINGTYPE))
{
@@ -100,38 +100,38 @@ namespace Svelto.ECS
return;
}
ProcessError(MSG, entityStructType, fieldType);
ProcessError(MSG, entityComponentType, fieldType);
}

static void ProcessError(string message, Type entityViewType, Type fieldType = null)
static void ProcessError(string message, Type entityComponentType, Type fieldType = null)
{
if (fieldType != null)
{
throw new EntityStructException(message, entityViewType, fieldType);
throw new EntityComponentException(message, entityComponentType, fieldType);
}

throw new EntityStructException(message, entityViewType);
throw new EntityComponentException(message, entityComponentType);
}

static readonly Type DISPATCHONCHANGETYPE = typeof(DispatchOnChange<>);
static readonly Type DISPATCHONSETTYPE = typeof(DispatchOnSet<>);
static readonly Type EGIDType = typeof(EGID);
static readonly Type EXCLUSIVEGROUPSTRUCTTYPE = typeof(ExclusiveGroup.ExclusiveGroupStruct);
static readonly Type SERIALIZABLE_ENTITY_STRUCT = typeof(SerializableEntityStruct);
static readonly Type EXCLUSIVEGROUPSTRUCTTYPE = typeof(ExclusiveGroupStruct);
static readonly Type SERIALIZABLE_ENTITY_STRUCT = typeof(SerializableEntityComponent);
static readonly Type STRINGTYPE = typeof(string);

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

public class EntityStructException : Exception
public class EntityComponentException : Exception
{
public EntityStructException(string message, Type entityViewType, Type type) :
base(message.FastConcat(" entity view: '", entityViewType.ToString(), "', field: '", type.ToString()))
public EntityComponentException(string message, Type entityComponentType, Type type) :
base(message.FastConcat(" entity view: '", entityComponentType.ToString(), "', field: '", type.ToString()))
{
}

public EntityStructException(string message, Type entityViewType) :
base(message.FastConcat(" entity view: ", entityViewType.ToString()))
public EntityComponentException(string message, Type entityComponentType) :
base(message.FastConcat(" entity view: ", entityComponentType.ToString()))
{
}
}

+ 0
- 8
Svelto.ECS/EntityBuilder.cs.rej View File

@@ -1,8 +0,0 @@
diff a/Assets/Svelto/Svelto.ECS/EntityBuilder.cs b/Assets/Svelto/Svelto.ECS/EntityBuilder.cs (rejected hunks)
@@ -1,5 +1,6 @@
using System;
using System.Collections.Generic;
+using DBC.ECS;
using Svelto.DataStructures;
using Svelto.ECS.Hybrid;
using Svelto.ECS.Internal;

+ 370
- 134
Svelto.ECS/EntityCollection.cs View File

@@ -1,82 +1,154 @@
using System;
using System.Collections;
using System.Collections.Generic;
using System.Runtime.CompilerServices;
using System.Runtime.InteropServices;
using System.Threading;
using Svelto.DataStructures;

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

public EntityIterator GetEnumerator()
public EntityCollection(MB<T> buffer, uint count)
{
_buffer = buffer;
_count = count;
}

public uint count => _count;

readonly MB<T> _buffer;
readonly uint _count;

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

readonly T[] _array;
readonly uint _count;
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public NB<NT> ToNativeBuffer<NT>() where NT : unmanaged, T
{
return new NB<NT>(Unsafe.As<NT[]>(_buffer.ToManagedArray()), _count);
}

[MethodImpl(MethodImplOptions.AggressiveInlining)]
public MB<T> ToBuffer(out uint count)
{
count = _count;
return _buffer;
}

public struct EntityIterator : IEnumerator<T>
public ref T this[uint i]
{
public EntityIterator(T[] array, uint count) : this()
[MethodImpl(MethodImplOptions.AggressiveInlining)]
get => ref _buffer[i];
}

public ref T this[int i]
{
[MethodImpl(MethodImplOptions.AggressiveInlining)]
get => ref _buffer[i];
}

[MethodImpl(MethodImplOptions.AggressiveInlining)]
public EntityIterator GetEnumerator() { return new EntityIterator(_buffer, _count); }

public struct EntityIterator
{
public EntityIterator(MB<T> array, uint count) : this()
{
_array = array;
_array = array.ToManagedArray();
_count = count;
_index = -1;
}

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

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

public ref T Current => ref _array[_index];

T IEnumerator<T>.Current => throw new NotImplementedException();
object IEnumerator.Current => throw new NotImplementedException();

public void Dispose() {}

readonly T[] _array;
readonly T[] _array;
readonly uint _count;
int _index;
int _index;
}
}

public struct EntityCollection<T1, T2>
where T1 : IEntityComponent where T2 : IEntityComponent
{
public EntityCollection(in (T1[], T2[]) array, uint count)
public EntityCollection(in EntityCollection<T1> array1, in EntityCollection<T2> array2)
{
_array = array;
_count = count;
_array1 = array1;
_array2 = array2;
}

public EntityIterator GetEnumerator()
public uint count => _array1.count;
public EntityCollection<T2> Item2
{
[MethodImpl(MethodImplOptions.AggressiveInlining)]
get => _array2;
}

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

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()
{
return new EntityIterator(_array, _count);
var bufferTuple = new BT<MB<T1>, MB<T2>>
(_array1.ToBuffer(out _), _array2.ToBuffer(out _), count);
return bufferTuple;
}

readonly (T1[], T2[]) _array;
readonly uint _count;
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;
}

public struct EntityIterator : IEnumerator<ValueRef<T1, T2>>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public EntityIterator GetEnumerator()
{
public EntityIterator((T1[], T2[]) array, uint count) : this()
return new EntityIterator(this);
}

public struct EntityIterator
{
public EntityIterator(in EntityCollection<T1, T2> array1) : this()
{
_array = array;
_count = count;
_array1 = array1;
_count = array1.count;
_index = -1;
}

[MethodImpl(MethodImplOptions.AggressiveInlining)]
public bool MoveNext()
{
return ++_index < _count;
@@ -87,86 +159,158 @@ namespace Svelto.ECS
_index = -1;
}

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

ValueRef<T1, T2> IEnumerator<ValueRef<T1, T2>>. Current => throw new NotImplementedException();
object IEnumerator.Current => throw new NotImplementedException();
readonly EntityCollection<T1, T2> _array1;
readonly uint _count;
int _index;
}
}

public void Dispose() {}
public struct EntityCollection<T1, T2, T3>
where T3 : IEntityComponent where T2 : IEntityComponent where T1 : IEntityComponent
{
public EntityCollection(
in EntityCollection<T1> array1, in EntityCollection<T2> array2,
in EntityCollection<T3> array3)
{
_array1 = array1;
_array2 = array2;
_array3 = array3;
}

readonly (T1[], T2[]) _array;
readonly uint _count;
int _index;
public EntityCollection<T1> Item1
{
[MethodImpl(MethodImplOptions.AggressiveInlining)]
get => _array1;
}

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

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

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(out _), _array2.ToBuffer(out _), _array3.ToBuffer(out _), 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 EntityCollection<T1, T2, T3>
public struct EntityCollections<T> where T : struct, IEntityComponent
{
public EntityCollection(in (T1[], T2[], T3[]) array, uint count)
public EntityCollections(EntitiesDB db, ExclusiveGroup[] groups) : this()
{
_array = array;
_count = count;
_db = db;
_groups = groups;
}

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

readonly (T1[], T2[], T3[]) _array;
readonly uint _count;
readonly EntitiesDB _db;
readonly ExclusiveGroup[] _groups;

public struct EntityIterator : IEnumerator<ValueRef<T1, T2, T3>>
public struct EntityGroupsIterator
{
public EntityIterator((T1[], T2[], T3[]) array, uint count) : this()
public EntityGroupsIterator(EntitiesDB db, ExclusiveGroup[] groups) : this()
{
_array = array;
_count = count;
_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 ValueRef<T1, T2, T3> Current => new ValueRef<T1, T2, T3>(_array, (uint) _index);

ValueRef<T1, T2, T3> IEnumerator<ValueRef<T1, T2, T3>>.Current => throw new NotImplementedException();
object IEnumerator. Current => throw new NotImplementedException();
public ref T Current => ref _array[(uint) _index];

public void Dispose() {}
readonly EntitiesDB _db;
readonly ExclusiveGroup[] _groups;

readonly (T1[], T2[], T3[]) _array;
readonly uint _count;
int _index;
EntityCollection<T> _array;
uint _count;
int _index;
int _indexGroup;
}
}
public struct EntityCollections<T> where T : struct, IEntityStruct

public struct EntityCollections<T1, T2>
where T1 : struct, IEntityComponent where T2 : struct, IEntityComponent
{
public EntityCollections(IEntitiesDB db, ExclusiveGroup[] groups) : this()
public EntityCollections(EntitiesDB db, ExclusiveGroup[] groups) : this()
{
_db = db;
_groups = groups;
}

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

readonly IEntitiesDB _db;
readonly EntitiesDB _db;
readonly ExclusiveGroup[] _groups;

public struct EntityGroupsIterator : IEnumerator<T>
public struct EntityGroupsIterator
{
public EntityGroupsIterator(IEntitiesDB db, ExclusiveGroup[] groups) : this()
public EntityGroupsIterator(EntitiesDB db, ExclusiveGroup[] groups) : this()
{
_db = db;
_groups = groups;
@@ -176,58 +320,64 @@ namespace Svelto.ECS

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

return ++_index < _count;
return ++_index < _array1.count;
}

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

public ref T Current => ref _array[_index];

T IEnumerator<T>.Current => throw new NotImplementedException();
object IEnumerator.Current => throw new NotImplementedException();
_array1 = _db.QueryEntities<T1, T2>(_groups[0]);
}

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

readonly IEntitiesDB _db;
readonly EntitiesDB _db;
readonly ExclusiveGroup[] _groups;
int _index;
int _indexGroup;

T[] _array;
uint _count;
int _index;
int _indexGroup;
EntityCollection<T1, T2> _array1;
}
}

public struct EntityCollections<T1, T2> where T1 : struct, IEntityStruct where T2 : struct, IEntityStruct
public struct EntityCollections<T1, T2, T3>
where T1 : struct, IEntityComponent where T2 : struct, IEntityComponent where T3 : struct, IEntityComponent
{
public EntityCollections(IEntitiesDB db, ExclusiveGroup[] groups) : this()
public EntityCollections(EntitiesDB db, ExclusiveGroup[] groups) : this()
{
_db = db;
_groups = groups;
}

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

readonly IEntitiesDB _db;
readonly EntitiesDB _db;
readonly ExclusiveGroup[] _groups;

public struct EntityGroupsIterator : IEnumerator<ValueRef<T1, T2>>
public struct EntityGroupsIterator
{
public EntityGroupsIterator(IEntitiesDB db, ExclusiveGroup[] groups) : this()
public EntityGroupsIterator(EntitiesDB db, ExclusiveGroup[] groups) : this()
{
_db = db;
_groups = groups;
@@ -237,17 +387,13 @@ namespace Svelto.ECS

public bool MoveNext()
{
//attention, the while is necessary to skip empty groups
while (_index + 1 >= _count && ++_indexGroup < _groups.Length)
{
_index = -1;
var array1 = _db.QueryEntities<T1>(_groups[_indexGroup], out _count);
var array2 = _db.QueryEntities<T2>(_groups[_indexGroup], out var count1);
_array = (array1, array2);

#if DEBUG && !PROFILER
if (_count != count1)
throw new ECSException("number of entities in group doesn't match");
#endif
_array1 = _db.QueryEntities<T1, T2, T3>(_groups[_indexGroup]);
_count = _array1.count;

}

return ++_index < _count;
@@ -258,69 +404,159 @@ namespace Svelto.ECS
_index = -1;
_indexGroup = -1;

var array1 = _db.QueryEntities<T1>(_groups[0], out _count);
var array2 = _db.QueryEntities<T2>(_groups[0], out var count1);
_array = (array1, array2);
#if DEBUG && !PROFILER
if (_count != count1)
throw new ECSException("number of entities in group doesn't match");
#endif
_array1 = _db.QueryEntities<T1, T2, T3>(_groups[0]);
_count = _array1.count;
}

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

ValueRef<T1, T2> IEnumerator<ValueRef<T1, T2>>.Current => throw new NotImplementedException();
object IEnumerator.Current => throw new NotImplementedException();

public void Dispose() {}

readonly IEntitiesDB _db;
readonly EntitiesDB _db;
readonly ExclusiveGroup[] _groups;
uint _count;
int _index;
int _indexGroup;
(T1[], T2[]) _array;

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

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

public void Dispose()
{
buffer1.Dispose();
buffer2.Dispose();
buffer3.Dispose();
buffer4.Dispose();
}
}

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

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

public void Dispose()
{
buffer1.Dispose();
buffer2.Dispose();
buffer3.Dispose();
}
}

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

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

public void Dispose()
{
buffer1.Dispose();
buffer2.Dispose();
}
}

public ref struct ValueRef<T1, T2> where T2 : IEntityComponent where T1 : IEntityComponent
{
readonly (T1[], T2[]) array;
readonly EntityCollection<T1, T2> array1;

readonly uint index;

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

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

public ref T2 entityComponentB
{
[MethodImpl(MethodImplOptions.AggressiveInlining)]
get => ref array1.Item2[index];
}
}
public struct ValueRef<T1, T2, T3>

public ref struct ValueRef<T1, T2, T3>
where T2 : IEntityComponent where T1 : IEntityComponent where T3 : IEntityComponent
{
readonly (T1[], T2[], T3[]) array;
readonly EntityCollection<T1, T2, T3> array1;

readonly uint index;

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

public ref T1 entityStructA => ref array.Item1[index];
public ref T2 entityStructB => ref array.Item2[index];
public ref T3 entityStructC => ref array.Item3[index];
public ref T1 entityComponentA
{
[MethodImpl(MethodImplOptions.AggressiveInlining)]
get => ref array1.Item1[index];
}

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

+ 61
- 0
Svelto.ECS/EntityComponentInitializer.cs View File

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

namespace Svelto.ECS
{
public readonly ref struct EntityComponentInitializer
{
public EntityComponentInitializer(EGID id, FasterDictionary<RefWrapper<Type>, ITypeSafeDictionary> group)
{
_group = group;
_ID = id;
}

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

var dictionary = (ITypeSafeDictionary<T>) typeSafeDictionary;

if (ComponentBuilder<T>.HAS_EGID)
SetEGIDWithoutBoxing<T>.SetIDWithoutBoxing(ref initializer, _ID);

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

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

return ref dictionary.GetOrCreate(_ID.entityID);
}
public ref T Get<T>() where T : struct, IEntityComponent
{
return ref (_group[new RefWrapper<Type>(ComponentBuilder<T>.ENTITY_COMPONENT_TYPE)] as ITypeSafeDictionary<T>)[
_ID.entityID];
}
public bool Has<T>() where T : struct, IEntityComponent
{
if (_group.TryGetValue(new RefWrapper<Type>(ComponentBuilder<T>.ENTITY_COMPONENT_TYPE),
out var typeSafeDictionary))
{
var dictionary = (ITypeSafeDictionary<T>) typeSafeDictionary;

if (dictionary.ContainsKey(_ID.entityID))
return true;
}

return false;
}
readonly EGID _ID;
readonly FasterDictionary<RefWrapper<Type>, ITypeSafeDictionary> _group;
}
}

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

@@ -2,7 +2,7 @@ namespace Svelto.ECS
{
public interface IEntityDescriptor
{
IEntityBuilder[] entitiesToBuild { get; }
IComponentBuilder[] componentsToBuild { get; }
}

static class EntityDescriptorTemplate<TType> where TType : IEntityDescriptor, new()


+ 24
- 39
Svelto.ECS/EntityFactory.cs View File

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

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

return group;
}

static FasterDictionary<RefWrapper<Type>, ITypeSafeDictionary> FetchEntityGroup(uint groupID,
EnginesRoot.DoubleBufferedEntitiesToAdd groupEntityViewsByType)
EnginesRoot.DoubleBufferedEntitiesToAdd groupEntityComponentsByType)
{
if (groupEntityViewsByType.current.TryGetValue(groupID, out var group) == false)
if (groupEntityComponentsByType.current.TryGetValue(groupID, out var group) == false)
{
group = new FasterDictionary<RefWrapper<Type>, ITypeSafeDictionary>();
groupEntityViewsByType.current.Add(groupID, group);
groupEntityComponentsByType.current.Add(groupID, group);
}

if (groupEntityViewsByType.currentEntitiesCreatedPerGroup.TryGetValue(groupID, out var value) == false)
groupEntityViewsByType.currentEntitiesCreatedPerGroup[groupID] = 0;
if (groupEntityComponentsByType.currentEntitiesCreatedPerGroup.TryGetValue(groupID, out var value) == false)
groupEntityComponentsByType.currentEntitiesCreatedPerGroup[groupID] = 0;
else
groupEntityViewsByType.currentEntitiesCreatedPerGroup[groupID] = value+1;
groupEntityComponentsByType.currentEntitiesCreatedPerGroup[groupID] = value+1;
return group;
}

static void BuildEntitiesAndAddToGroup(EGID entityID,
FasterDictionary<RefWrapper<Type>, ITypeSafeDictionary> group,
IEntityBuilder[] entityBuilders, IEnumerable<object> implementors)
IComponentBuilder[] entityBuilders, IEnumerable<object> implementors)
{
#if DEBUG && !PROFILER
#if DEBUG && !PROFILE_SVELTO
HashSet<Type> types = new HashSet<Type>();
#endif
InternalBuild(entityID, group, entityBuilders, implementors
#if DEBUG && !PROFILER
, types
#endif
);
}

static void InternalBuild(EGID entityID, FasterDictionary<RefWrapper<Type>, ITypeSafeDictionary> group,
IEntityBuilder[] entityBuilders, IEnumerable<object> implementors
#if DEBUG && !PROFILER
, HashSet<Type> types
#endif
)
{
var count = entityBuilders.Length;
#if DEBUG && !PROFILER
#if DEBUG && !PROFILE_SVELTO
for (var index = 0; index < count; ++index)
{
var entityViewType = entityBuilders[index].GetEntityType();
if (types.Contains(entityViewType))
var entityComponentType = entityBuilders[index].GetEntityComponentType();
if (types.Contains(entityComponentType))
{
throw new ECSException("EntityBuilders must be unique inside an EntityDescriptor");
}

types.Add(entityViewType);
types.Add(entityComponentType);
}
#endif
for (var index = 0; index < count; ++index)
{
var entityStructBuilder = entityBuilders[index];
var entityViewType = entityStructBuilder.GetEntityType();
var entityComponentBuilder = entityBuilders[index];
var entityComponentType = entityComponentBuilder.GetEntityComponentType();

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

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

//passing the undefined entityViewsByType inside the entityViewBuilder will allow it to be created with the
//passing the undefined entityComponentsByType inside the entityComponentBuilder will allow it to be created with the
//correct type and casted back to the undefined list. that's how the list will be eventually of the target
//type.
entityBuilder.BuildEntityAndAddToList(ref safeDictionary, entityID, implementors);
componentBuilder.BuildEntityAndAddToList(ref safeDictionary, entityID, implementors);

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

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

@@ -4,7 +4,7 @@ namespace Svelto.ECS.Internal
{
class EntityGroupNotFoundException : Exception
{
public EntityGroupNotFoundException(uint groupId, Type type)
public EntityGroupNotFoundException(Type type)
: base("entity group not found ".FastConcat(type.ToString()))
{
}


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

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

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

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

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

@@ -1,7 +1,7 @@
namespace Svelto.ECS
{
struct EntityStructInfoView: IEntityStruct
struct EntityInfoComponentView: IEntityComponent
{
public IEntityBuilder[] entitiesToBuild;
public IComponentBuilder[] componentsToBuild;
}
}

+ 91
- 48
Svelto.ECS/EntityStream.cs View File

@@ -1,4 +1,5 @@
using System;
using System.Runtime.InteropServices;
using Svelto.DataStructures;

namespace Svelto.ECS
@@ -14,25 +15,28 @@ namespace Svelto.ECS
/// one only
/// - you want to communicate between EnginesRoots
/// </summary>
class EntitiesStream : IDisposable
{
internal Consumer<T> GenerateConsumer<T>(string name, uint capacity) where T : unmanaged, IEntityStruct
internal Consumer<T> GenerateConsumer<T>(string name, uint capacity)
where T : unmanaged, IEntityComponent
{
if (_streams.ContainsKey(TypeRefWrapper<T>.wrapper) == false) _streams[TypeRefWrapper<T>.wrapper] = new EntityStream<T>();
if (_streams.ContainsKey(TypeRefWrapper<T>.wrapper) == false)
_streams[TypeRefWrapper<T>.wrapper] = new EntityStream<T>();

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

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

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

internal void PublishEntity<T>(ref T entity, EGID egid) where T : unmanaged, IEntityStruct
internal void PublishEntity<T>(ref T entity, EGID egid) where T : unmanaged, IEntityComponent
{
if (_streams.TryGetValue(TypeRefWrapper<T>.wrapper, out var typeSafeStream))
(typeSafeStream as EntityStream<T>).PublishEntity(ref entity, egid);
@@ -50,77 +54,96 @@ namespace Svelto.ECS
}

interface ITypeSafeStream
{}
{ }

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

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

_consumers.Add(consumer);

return consumer;
}

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

_consumers.Add(consumer);

return consumer;
}

public void RemoveConsumer(Consumer<T> consumer)
{
_consumers.UnorderedRemove(consumer);
}

readonly FasterListThreadSafe<Consumer<T>> _consumers = new FasterListThreadSafe<Consumer<T>>();
readonly ThreadSafeFasterList<Consumer<T>> _consumers;
}

public struct Consumer<T> : IDisposable where T : unmanaged, IEntityStruct
public struct Consumer<T> :IDisposable where T : unmanaged, IEntityComponent
{
internal Consumer(string name, uint capacity, EntityStream<T> stream):this()
internal Consumer(string name, uint capacity) : this()
{
#if DEBUG && !PROFILER
_name = name;
unsafe
{
#if DEBUG && !PROFILE_SVELTO
_name = name;
#endif
_ringBuffer = new RingBuffer<ValueTuple<T, EGID>>((int) capacity,
#if DEBUG && !PROFILER
_name
_ringBuffer = new RingBuffer<ValueTuple<T, EGID>>((int) capacity,
#if DEBUG && !PROFILE_SVELTO
_name
#else
string.Empty
#endif
);

_stream = stream;
mustBeDisposed = Marshal.AllocHGlobal(sizeof(bool));
*((bool*) mustBeDisposed) = false;
}
}

internal Consumer(ExclusiveGroup group, string name, uint capacity, EntityStream<T> stream) : this(name,
capacity, stream)
internal Consumer(ExclusiveGroup group, string name, uint capacity) : this(name, capacity)
{
_group = group;
_hasGroup = true;
this.@group = @group;
hasGroup = true;
}

internal void Enqueue(in T entity, in EGID egid)
@@ -146,18 +169,38 @@ namespace Svelto.ECS

return tryDequeue;
}
public void Flush() { _ringBuffer.Reset(); }
public void Dispose() { _stream.RemoveConsumer(this); }
public uint Count() { return (uint) _ringBuffer.Count; }

readonly RingBuffer<ValueTuple<T, EGID>> _ringBuffer;
readonly EntityStream<T> _stream;
public void Flush()
{
_ringBuffer.Reset();
}

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

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

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

internal readonly ExclusiveGroup _group;
internal readonly bool _hasGroup;
internal readonly ExclusiveGroup @group;
internal readonly bool hasGroup;
internal IntPtr mustBeDisposed;

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

+ 0
- 75
Svelto.ECS/EntityStructInitializer.cs View File

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

namespace Svelto.ECS
{
public ref struct EntityStructInitializer
{
public EntityStructInitializer(EGID id, FasterDictionary<RefWrapper<Type>, ITypeSafeDictionary> group)
{
_group = group;
_ID = id;
}

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

var dictionary = (TypeSafeDictionary<T>) typeSafeDictionary;

if (EntityBuilder<T>.HAS_EGID)
SetEGIDWithoutBoxing<T>.SetIDWithoutBoxing(ref initializer, _ID);

if (dictionary.TryFindIndex(_ID.entityID, out var findElementIndex))
dictionary.GetDirectValue(findElementIndex) = initializer;
}
public void CopyFrom<T>(T initializer) where T : struct, IEntityStruct
{
var dictionary = (TypeSafeDictionary<T>) _group[new RefWrapper<Type>(EntityBuilder<T>.ENTITY_VIEW_TYPE)];

if (EntityBuilder<T>.HAS_EGID)
SetEGIDWithoutBoxing<T>.SetIDWithoutBoxing(ref initializer, _ID);

dictionary[_ID.entityID] = initializer;
}

public ref T GetOrCreate<T>() where T : struct, IEntityStruct
{
ref var entityDictionary = ref _group.GetOrCreate(new RefWrapper<Type>(EntityBuilder<T>.ENTITY_VIEW_TYPE)
, () => new TypeSafeDictionary<T>());
var dictionary = (TypeSafeDictionary<T>) entityDictionary;

return ref dictionary.GetOrCreate(_ID.entityID);
}
public T Get<T>() where T : struct, IEntityStruct
{
return (_group[new RefWrapper<Type>(EntityBuilder<T>.ENTITY_VIEW_TYPE)] as TypeSafeDictionary<T>)[_ID.entityID];
}

public bool Has<T>() where T : struct, IEntityStruct
{
if (_group.TryGetValue(new RefWrapper<Type>(EntityBuilder<T>.ENTITY_VIEW_TYPE),
out var typeSafeDictionary))
{
var dictionary = (TypeSafeDictionary<T>) typeSafeDictionary;

if (dictionary.ContainsKey(_ID.entityID))
return true;
}

return false;
}

public static EntityStructInitializer CreateEmptyInitializer()
{
return new EntityStructInitializer(new EGID(), new FasterDictionary<RefWrapper<Type>, ITypeSafeDictionary>());
}

readonly EGID _ID;
readonly FasterDictionary<RefWrapper<Type>, ITypeSafeDictionary> _group;
}
}

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

@@ -2,7 +2,7 @@ using System;

namespace Svelto.ECS.Schedulers
{
public interface IEntitySubmissionScheduler: IDisposable
public interface IEntitiesSubmissionScheduler: IDisposable
{
EnginesRoot.EntitiesSubmitter onTick { set; }
}

+ 7
- 7
Svelto.ECS/EntitySubmitOperation.cs View File

@@ -1,5 +1,4 @@
using System;
using System.Diagnostics;

namespace Svelto.ECS
{
@@ -9,21 +8,21 @@ namespace Svelto.ECS
: IEquatable<EntitySubmitOperation>
{
public readonly EntitySubmitOperationType type;
public readonly IEntityBuilder[] builders;
public readonly IComponentBuilder[] builders;
public readonly EGID fromID;
public readonly EGID toID;
#if DEBUG && !PROFILER
public StackFrame trace;
#if DEBUG && !PROFILE_SVELTO
public System.Diagnostics.StackFrame trace;
#endif

public EntitySubmitOperation(EntitySubmitOperationType operation, EGID from, EGID to,
IEntityBuilder[] builders = null)
IComponentBuilder[] builders = null)
{
type = operation;
this.builders = builders;
fromID = from;
toID = to;
#if DEBUG && !PROFILER
#if DEBUG && !PROFILE_SVELTO
trace = default;
#endif
}
@@ -48,6 +47,7 @@ namespace Svelto.ECS
{
Swap,
Remove,
RemoveGroup
RemoveGroup,
SwapGroup
}
}

+ 45
- 51
Svelto.ECS/EntityViewUtility.cs View File

@@ -1,58 +1,66 @@
using System;
using System.Collections.Generic;
using Svelto.DataStructures;
using Svelto.ECS.Internal;
using Svelto.Utilities;

namespace Svelto.ECS
{
#if DEBUG && !PROFILER
#if DEBUG && !PROFILE_SVELTO
struct ECSTuple<T1, T2>
{
public readonly T1 implementorType;
public readonly T1 instance;
public T2 numberOfImplementations;

public ECSTuple(T1 implementor, T2 v)
{
implementorType = implementor;
instance = implementor;
numberOfImplementations = v;
}
}
#endif

static class EntityViewUtility
static class EntityComponentUtility
{
const string DUPLICATE_IMPLEMENTOR_ERROR =
"<color=teal>Svelto.ECS</color> the same component is implemented with more than one implementor. This is "
+ "considered an error and MUST be fixed. ";

const string NULL_IMPLEMENTOR_ERROR =
"<color=teal>Svelto.ECS</color> Null implementor, please be careful about the implementors passed to avoid "
+ "performance loss ";

public static void FillEntityView<T>(this IEntityBuilder entityBuilder
, ref T entityView
, FasterList<KeyValuePair<Type, ActionCast<T>>>
entityViewBlazingFastReflection
, IEnumerable<object> implementors,
#if DEBUG && !PROFILER
Dictionary<Type, ECSTuple<object, int>> implementorsByType
const string NOT_FOUND_EXCEPTION =
"<color=teal>Svelto.ECS</color> Implementor not found for an EntityComponent. ";

public static void FillEntityComponent<T>
(this IComponentBuilder componentBuilder, ref T entityComponent
, FasterList<KeyValuePair<Type, FastInvokeActionCast<T>>> entityComponentBlazingFastReflection
, IEnumerable<object> implementors
#if DEBUG && !PROFILE_SVELTO
,Dictionary<Type, ECSTuple<object, int>> implementorsByType
#else
Dictionary<Type, object> implementorsByType
, Dictionary<Type, object> implementorsByType
#endif
, Dictionary<Type, Type[]> cachedTypes
)
, Dictionary<Type, Type[]> cachedTypeInterfaces)
{
//efficient way to collect the fields of every EntityViewType
var setters =
FasterList<KeyValuePair<Type, ActionCast<T>>>.NoVirt.ToArrayFast(entityViewBlazingFastReflection, out var count);
//efficient way to collect the fields of every EntityComponentType
var setters = FasterList<KeyValuePair<Type, FastInvokeActionCast<T>>>.NoVirt.ToArrayFast(
entityComponentBlazingFastReflection, out var count);

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

if (cachedTypes.TryGetValue(type, out var interfaces) == false)
interfaces = cachedTypes[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 && !PROFILER
#if DEBUG && !PROFILE_SVELTO
if (implementorsByType.TryGetValue(componentType, out var implementorData))
{
implementorData.numberOfImplementations++;
@@ -65,11 +73,11 @@ namespace Svelto.ECS
#endif
}
}
#if DEBUG && !PROFILER
#if DEBUG && !PROFILE_SVELTO
else
{
Console.Log(NULL_IMPLEMENTOR_ERROR.FastConcat(" entityView ",
entityBuilder.GetEntityType().ToString()));
Console.Log(NULL_IMPLEMENTOR_ERROR.FastConcat(" entityComponent ",
componentBuilder.GetEntityComponentType().ToString()));
}
#endif
}
@@ -79,47 +87,33 @@ namespace Svelto.ECS
var fieldSetter = setters[i];
var fieldType = fieldSetter.Key;

#if DEBUG && !PROFILER
ECSTuple<object, int> component;
#if DEBUG && !PROFILE_SVELTO
ECSTuple<object, int> implementor;
#else
object component;
object implementor;
#endif

if (implementorsByType.TryGetValue(fieldType, out component) == false)
if (implementorsByType.TryGetValue(fieldType, out implementor) == false)
{
var e = new ECSException(NOT_FOUND_EXCEPTION + " Component Type: " + fieldType.Name +
" - EntityView: " + entityBuilder.GetEntityType().Name);
var e = new ECSException(NOT_FOUND_EXCEPTION + " Component Type: " + fieldType.Name
+ " - EntityComponent: " + componentBuilder.GetEntityComponentType().Name);

throw e;
}
#if DEBUG && !PROFILER
if (component.numberOfImplementations > 1)
#if DEBUG && !PROFILE_SVELTO
if (implementor.numberOfImplementations > 1)
throw new ECSException(DUPLICATE_IMPLEMENTOR_ERROR.FastConcat(
"Component Type: ", fieldType.Name,
" implementor: ",
component.implementorType
.ToString()) +
" - EntityView: " +
entityBuilder.GetEntityType().Name);
"Component Type: ", fieldType.Name, " implementor: ", implementor.instance.ToString()) +
" - EntityComponent: " + componentBuilder.GetEntityComponentType().Name);
#endif
#if DEBUG && !PROFILER
fieldSetter.Value(ref entityView, component.implementorType);
#if DEBUG && !PROFILE_SVELTO
fieldSetter.Value(ref entityComponent, implementor.instance);
#else
fieldSetter.Value(ref entityView, component);
fieldSetter.Value(ref entityComponent, implementor);
#endif
}

implementorsByType.Clear();
}

const string DUPLICATE_IMPLEMENTOR_ERROR =
"<color=teal>Svelto.ECS</color> the same component is implemented with more than one implementor. This is " +
"considered an error and MUST be fixed. ";

const string NULL_IMPLEMENTOR_ERROR =
"<color=teal>Svelto.ECS</color> Null implementor, please be careful about the implementors passed to avoid " +
"performance loss ";

const string NOT_FOUND_EXCEPTION = "<color=teal>Svelto.ECS</color> Implementor not found for an EntityView. ";
}
}

+ 38
- 120
Svelto.ECS/ExclusiveGroup.cs View File

@@ -1,10 +1,27 @@
using System;
using System.Collections.Generic;
using Svelto.ECS.Internal;

#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.
///
@@ -17,26 +34,12 @@ namespace Svelto.ECS
/// public static ExclusiveGroup[] GroupOfGroups = { MyExclusiveGroup1, ...}; //for each on this!
/// }
/// </summary>
///

///use this like:
/// public class TriggersGroup : ExclusiveGroup<TriggersGroup> {}
public abstract class NamedExclusiveGroup<T>:ExclusiveGroup
{
public static ExclusiveGroup Group = new ExclusiveGroup();
public static string name = typeof(T).FullName;

public NamedExclusiveGroup() { }

public NamedExclusiveGroup(string recognizeAs) : base(recognizeAs)
{}

public NamedExclusiveGroup(ushort range) : base(range)
{}
}

///To debug it use in your debug window: Svelto.ECS.Debugger.EGID.GetGroupNameFromId(groupID)
public class ExclusiveGroup
{
public const uint MaxNumberOfExclusiveGroups = 2 << 20;
public ExclusiveGroup()
{
_group = ExclusiveGroupStruct.Generate();
@@ -46,7 +49,7 @@ namespace Svelto.ECS
{
_group = ExclusiveGroupStruct.Generate();

_serialisedGroups.Add(recognizeAs, _group);
_knownGroups.Add(recognizeAs, _group);
}

public ExclusiveGroup(ushort range)
@@ -61,7 +64,7 @@ namespace Svelto.ECS
{
return group._group;
}
public static explicit operator uint(ExclusiveGroup group)
{
return group._group;
@@ -71,118 +74,33 @@ namespace Svelto.ECS
{
#if DEBUG
if (a._range == 0)
throw new ECSException("adding values to a not ranged ExclusiveGroup");
throw new ECSException($"Adding values to a not ranged ExclusiveGroup: {(uint)a}");
if (b >= a._range)
throw new ECSException("Using out of range group");
#endif
throw new ECSException($"Using out of range group: {(uint)a} + {b}");
#endif
return a._group + b;
}

readonly ExclusiveGroupStruct _group;

//I use this as parameter because it must not be possible to pass null Exclusive Groups.
public struct ExclusiveGroupStruct : IEquatable<ExclusiveGroupStruct>, IComparable<ExclusiveGroupStruct>,
IEqualityComparer<ExclusiveGroupStruct>
public static ExclusiveGroupStruct Search(string holderGroupName)
{
public static bool operator ==(ExclusiveGroupStruct c1, ExclusiveGroupStruct c2)
{
return c1.Equals(c2);
}

public static bool operator !=(ExclusiveGroupStruct c1, ExclusiveGroupStruct c2)
{
return c1.Equals(c2) == false;
}

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

public int CompareTo(ExclusiveGroupStruct other)
{
return other._id.CompareTo(_id);
}

public bool Equals(ExclusiveGroupStruct x, ExclusiveGroupStruct y)
{
return x._id == y._id;
}

public int GetHashCode(ExclusiveGroupStruct obj)
{
return _id.GetHashCode();
}

internal static ExclusiveGroupStruct Generate()
{
ExclusiveGroupStruct groupStruct;

groupStruct._id = _globalId;
DBC.ECS.Check.Require(_globalId + 1 < ushort.MaxValue, "too many exclusive groups created");
_globalId++;

return groupStruct;
}

/// <summary>
/// Use this constructor to reserve N groups
/// </summary>
internal ExclusiveGroupStruct(ushort range)
{
_id = _globalId;
DBC.ECS.Check.Require(_globalId + range < ushort.MaxValue, "too many exclusive groups created");
_globalId += range;
}

internal ExclusiveGroupStruct(uint groupID)
{
_id = groupID;
}

public ExclusiveGroupStruct(byte[] data, uint pos)
{
_id = (uint)(
data[pos++]
| data[pos++] << 8
| data[pos++] << 16
| data[pos++] << 24
);
DBC.ECS.Check.Ensure(_id < _globalId, "Invalid group ID deserialiased");
}

public static implicit operator uint(ExclusiveGroupStruct groupStruct)
{
return groupStruct._id;
}

public static ExclusiveGroupStruct operator+(ExclusiveGroupStruct a, uint b)
{
var group = new ExclusiveGroupStruct();

group._id = a._id + b;

return group;
}
if (_knownGroups.ContainsKey(holderGroupName) == false)
throw new Exception("Named Group Not Found ".FastConcat(holderGroupName));

uint _id;
static uint _globalId;
return _knownGroups[holderGroupName];
}

public static ExclusiveGroupStruct Search(string holderGroupName)
public override string ToString()
{
if (_serialisedGroups.ContainsKey(holderGroupName) == false)
throw new Exception("Named Group Not Found ".FastConcat(holderGroupName));

return _serialisedGroups[holderGroupName];
return _group.ToString();
}

static readonly Dictionary<string, ExclusiveGroupStruct> _serialisedGroups = new Dictionary<string,
static readonly Dictionary<string, ExclusiveGroupStruct> _knownGroups = new Dictionary<string,
ExclusiveGroupStruct>();

#if DEBUG
readonly ushort _range;
#endif
#endif
readonly ExclusiveGroupStruct _group;
}
}

@@ -260,6 +178,6 @@ namespace Svelto.ECS
}

#if DEBUG
static string[] groupNames = new string[ushort.MaxValue];
static string[] groupNames = new string[ExclusiveGroup.MaxNumberOfExclusiveGroups];
#endif
#endif

+ 114
- 0
Svelto.ECS/ExclusiveGroupStruct.cs View File

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

namespace Svelto.ECS
{
[StructLayout(LayoutKind.Explicit, Size = 4)]
public struct ExclusiveGroupStruct : IEquatable<ExclusiveGroupStruct>, IComparable<ExclusiveGroupStruct>,
IEqualityComparer<ExclusiveGroupStruct>
{
public override bool Equals(object obj)
{
return obj is ExclusiveGroupStruct other && Equals(other);
}

public override int GetHashCode()
{
return (int) _id;
}

public static bool operator ==(ExclusiveGroupStruct c1, ExclusiveGroupStruct c2)
{
return c1.Equals(c2);
}

public static bool operator !=(ExclusiveGroupStruct c1, ExclusiveGroupStruct c2)
{
return c1.Equals(c2) == false;
}

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

public int CompareTo(ExclusiveGroupStruct other)
{
return other._id.CompareTo(_id);
}

public bool Equals(ExclusiveGroupStruct x, ExclusiveGroupStruct y)
{
return x._id == y._id;
}

public int GetHashCode(ExclusiveGroupStruct obj)
{
return _id.GetHashCode();
}

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

internal static ExclusiveGroupStruct Generate(byte bitmask = 0)
{
ExclusiveGroupStruct groupStruct;

groupStruct._id = _globalId;
groupStruct._bytemask = bitmask;
DBC.ECS.Check.Require(_globalId + 1 < ExclusiveGroup.MaxNumberOfExclusiveGroups, "too many exclusive groups created");
_globalId++;

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

/// <summary>
/// Use this constructor to reserve N groups
/// </summary>
internal ExclusiveGroupStruct(ushort range):this()
{
_id = _globalId;
DBC.ECS.Check.Require(_globalId + range < ExclusiveGroup.MaxNumberOfExclusiveGroups, "too many exclusive groups created");
_globalId += range;
}

internal ExclusiveGroupStruct(uint groupID):this()
{
_id = groupID;
}

public ExclusiveGroupStruct(byte[] data, uint pos):this()
{
_id = (uint)(
data[pos]
| data[++pos] << 8
| data[++pos] << 16
);
_bytemask = (byte) (data[++pos] << 24);

DBC.ECS.Check.Ensure(_id < _globalId, "Invalid group ID deserialiased");
}

public static implicit operator uint(ExclusiveGroupStruct groupStruct)
{
return groupStruct._id;
}
public static ExclusiveGroupStruct operator+(ExclusiveGroupStruct a, uint b)
{
var group = new ExclusiveGroupStruct {_id = a._id + b};

return @group;
}

[FieldOffset(0)] uint _id;
[FieldOffset(3)] byte _bytemask;
static uint _globalId;
}
}

+ 0
- 61
Svelto.ECS/ExecuteOnEntitiesDB.cs View File

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

namespace Svelto.ECS.Internal
{
partial class EntitiesDB
{
public void ExecuteOnAllEntities<T>(Action<T[], ExclusiveGroup.ExclusiveGroupStruct, uint, IEntitiesDB> action)
where T : struct, IEntityStruct
{
var type = typeof(T);

if (_groupsPerEntity.TryGetValue(new RefWrapper<Type>(type), out var dictionary))
{
foreach (var pair in dictionary)
{
var entities = (pair.Value as TypeSafeDictionary<T>).GetValuesArray(out var innerCount);

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

public void ExecuteOnAllEntities
<T, W>(W value, Action<T[], ExclusiveGroup.ExclusiveGroupStruct, uint, IEntitiesDB, W> action)
where T : struct, IEntityStruct
{
var type = typeof(T);

if (_groupsPerEntity.TryGetValue(new RefWrapper<Type>(type), out var dic))
{
foreach (var pair in dic)
{
var entities = (pair.Value as TypeSafeDictionary<T>).GetValuesArray(out var innerCount);

if (innerCount > 0)
action(entities, new ExclusiveGroup.ExclusiveGroupStruct(pair.Key), innerCount, this, value);
}
}
}
public void ExecuteOnAllEntities
<T, W>(ref W value, ExecuteOnAllEntitiesAction<T, W> action)
where T : struct, IEntityStruct
{
var type = typeof(T);

if (_groupsPerEntity.TryGetValue(new RefWrapper<Type>(type), out var dic))
{
foreach (var pair in dic)
{
var entities = (pair.Value as TypeSafeDictionary<T>).GetValuesArray(out var innerCount);

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

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

@@ -17,7 +17,7 @@ namespace Svelto.ECS
$"SerializableEntityDescriptors cannot be used as base entity descriptor: {typeof(TType)}");
}

public ExtendibleEntityDescriptor(IEntityBuilder[] extraEntities)
public ExtendibleEntityDescriptor(IComponentBuilder[] extraEntities)
{
_dynamicDescriptor = new DynamicEntityDescriptor<TType>(extraEntities);
}
@@ -34,14 +34,14 @@ namespace Svelto.ECS
return this;
}

public ExtendibleEntityDescriptor<TType> ExtendWith(IEntityBuilder[] extraEntities)
public ExtendibleEntityDescriptor<TType> ExtendWith(IComponentBuilder[] extraEntities)
{
_dynamicDescriptor.ExtendWith(extraEntities);

return this;
}

public IEntityBuilder[] entitiesToBuild => _dynamicDescriptor.entitiesToBuild;
public IComponentBuilder[] componentsToBuild => _dynamicDescriptor.componentsToBuild;

DynamicEntityDescriptor<TType> _dynamicDescriptor;
}

+ 19
- 0
Svelto.ECS/Extensions/ProcessorCount.cs View File

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

namespace Svelto.ECS
{
internal static class ProcessorCount
{
static readonly int processorCount = Environment.ProcessorCount;

public static int BatchSize(uint totalIterations)
{
var iterationsPerBatch = totalIterations / processorCount;

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

+ 70
- 0
Svelto.ECS/Extensions/Svelto/EntityDBExtensions.cs View File

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

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
{
return new NativeGroupsEnumerable<T1, T2>(db, groups);
}

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

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

+ 117
- 0
Svelto.ECS/Extensions/Svelto/NativeAllGroupsEnumerable.cs View File

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

namespace Svelto.ECS
{
public struct NativeAllGroupsEnumerable<T1> where T1 : unmanaged, IEntityComponent
{
public NativeAllGroupsEnumerable(EntitiesDB db)
{
_db = db;
}

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

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

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

return true;
}

return false;
}

public void Reset()
{
}

public NB<T1> Current => _array;

readonly FasterDictionary<uint, ITypeSafeDictionary>.FasterDictionaryKeyValueEnumerator _db;

NB<T1> _array;
}

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

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

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

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

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

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

return true;
}

return false;
}

public void Reset()
{
}

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

readonly FasterDictionary<uint, ITypeSafeDictionary>.FasterDictionaryKeyValueEnumerator _db;

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

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

readonly EntitiesDB _db;
}
#endif
}

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

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

namespace Svelto.ECS
{
public 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 entityCollection = _entitiesDB.QueryEntities<T1, T2, T3>(_groups[_indexGroup]);
if (entityCollection.count == 0) continue;
var entityCollection2 = _entitiesDB.QueryEntities<T4>(_groups[_indexGroup]);
if (entityCollection2.count == 0) continue;
DBC.ECS.Check.Assert(entityCollection.count == entityCollection2.count, "congratulation, you found a bug in Svelto, please report it");

BT<NB<T1>, NB<T2>, NB<T3>> array = entityCollection.ToNativeBuffers<T1, T2, T3>();
NB<T4> array2 = entityCollection2.ToNativeBuffer<T4>();
_array= new BT<NB<T1>, NB<T2>, NB<T3>, NB<T4>>(array.buffer1, array.buffer2, array.buffer3, array2, entityCollection.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 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)
{
_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, 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;
}
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;
}
}

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

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

+ 84
- 0
Svelto.ECS/Extensions/Unity/DOTS/DisposeJob.cs View File

@@ -0,0 +1,84 @@
#if UNITY_2019_2_OR_NEWER
using System;
using Unity.Jobs;

namespace Svelto.ECS.Extensions.Unity
{
public struct DisposeJob<T>:IJob where T:struct,IDisposable
{
public DisposeJob(in T disposable)
{
_entityCollection = disposable;
}

public void Execute()
{
try
{
_entityCollection.Dispose();
}
catch (Exception e)
{
Console.LogException(e, this.GetType().ToString().FastConcat(" "));
}
}
readonly T _entityCollection;
}
public struct DisposeJob<T1, T2>:IJob
where T1:struct,IDisposable where T2:struct,IDisposable
{
public DisposeJob(in T1 disposable1, in T2 disposable2)
{
_entityCollection1 = disposable1;
_entityCollection2 = disposable2;
}

public void Execute()
{
try
{
_entityCollection1.Dispose();
_entityCollection2.Dispose();
}
catch (Exception e)
{
Console.LogException(e, this.GetType().ToString().FastConcat(" "));
}
}
readonly T1 _entityCollection1;
readonly T2 _entityCollection2;
}
public struct DisposeJob<T1, T2, T3>:IJob
where T1:struct,IDisposable where T2:struct,IDisposable where T3:struct,IDisposable
{
public DisposeJob(in T1 disposable1, in T2 disposable2, in T3 disposable3)
{
_entityCollection1 = disposable1;
_entityCollection2 = disposable2;
_entityCollection3 = disposable3;
}

public void Execute()
{
try
{
_entityCollection1.Dispose();
_entityCollection2.Dispose();
_entityCollection3.Dispose();
}
catch (Exception e)
{
Console.LogException(e, this.GetType().ToString().FastConcat(" "));
}
}
readonly T1 _entityCollection1;
readonly T2 _entityCollection2;
readonly T3 _entityCollection3;
}
}
#endif

+ 265
- 0
Svelto.ECS/Extensions/Unity/DOTS/EnginesRoot.NativeOperation.cs View File

@@ -0,0 +1,265 @@
#if UNITY_ECS
using Svelto.Common;
using Svelto.DataStructures;
using Svelto.ECS.DataStructures;
using Svelto.ECS.DataStructures.Unity;
using Unity.Jobs.LowLevel.Unsafe;

namespace Svelto.ECS
{
public partial class EnginesRoot
{
//todo: I very likely don't need to create one for each native entity factory, the same can be reused
readonly AtomicRingBuffers _addOperationQueue =
new AtomicRingBuffers(Common.Allocator.Persistent, JobsUtility.MaxJobThreadCount + 1);

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

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

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

return new NativeEntityRemove(_removeOperationQueue, _nativeRemoveOperations.count - 1);
}
NativeEntitySwap ProvideNativeEntitySwapQueue<T>() 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));

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

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

return new NativeEntityFactory(_addOperationQueue, _nativeAddOperations.count - 1);
}

void NativeOperationSubmission(in PlatformProfiler profiler)
{
using (profiler.Sample("Native Remove/Swap Operations"))
{
for (int i = 0; i < _removeOperationQueue.count; i++)
{
ref var buffer = ref _removeOperationQueue.GetBuffer(i);

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

for (int i = 0; i < _swapOperationQueue.count; i++)
{
ref var buffer = ref _swapOperationQueue.GetBuffer(i);

while (buffer.IsEmpty() == false)
{
var componentsIndex = buffer.Dequeue<uint>();
var entityEGID = buffer.Dequeue<DoubleEGID>();
CheckRemoveEntityID(entityEGID.@from);
CheckAddEntityID(entityEGID.to);
QueueEntitySubmitOperation(new EntitySubmitOperation(
EntitySubmitOperationType.Swap, entityEGID.@from, entityEGID.to
, _nativeSwapOperations[componentsIndex].entityComponents));
}
}
}
using (profiler.Sample("Native Add Operations"))
{
for (int i = 0; i < _addOperationQueue.count; i++)
{
ref var buffer = ref _addOperationQueue.GetBuffer(i);
while (buffer.IsEmpty() == false)
{
var componentsIndex = buffer.Dequeue<uint>();
var egid = buffer.Dequeue<EGID>();
var componentCounts = buffer.Dequeue<uint>();
EntityComponentInitializer init =
BuildEntity(egid, _nativeAddOperations[componentsIndex].components);

while (componentCounts > 0)
{
componentCounts--;

var typeID = buffer.Dequeue<uint>();

IFiller entityBuilder = EntityComponentIDMap.GetTypeFromID(typeID);

//after the typeID, I expect the serialized component
entityBuilder.FillFromByteArray(init, buffer);
}
}
}
}
}

void AllocateNativeOperations()
{
_nativeRemoveOperations = new FasterList<NativeOperationRemove>();
_nativeSwapOperations = new FasterList<NativeOperationSwap>();
_nativeAddOperations = new FasterList<NativeOperationBuild>();
}

FasterList<NativeOperationRemove> _nativeRemoveOperations;
FasterList<NativeOperationSwap> _nativeSwapOperations;
FasterList<NativeOperationBuild> _nativeAddOperations;
}

readonly struct DoubleEGID
{
internal readonly EGID from;
internal readonly EGID to;

public DoubleEGID(EGID from1, EGID to1)
{
from = from1;
to = to1;
}
}

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

internal NativeEntityRemove(AtomicRingBuffers 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 AtomicRingBuffers _swapQueue;
readonly uint _indexSwap;

internal NativeEntitySwap(AtomicRingBuffers 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 AtomicRingBuffers _addOperationQueue;
readonly uint _index;

internal NativeEntityFactory(AtomicRingBuffers 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
{
internal readonly IComponentBuilder[] components;

public NativeOperationBuild(IComponentBuilder[] descriptorEntityComponentsToBuild)
{
components = descriptorEntityComponentsToBuild;
}
}

readonly struct NativeOperationRemove
{
internal readonly IComponentBuilder[] entityComponents;

public NativeOperationRemove(IComponentBuilder[] descriptorEntitiesToBuild)
{
entityComponents = descriptorEntitiesToBuild;
}
}

readonly struct NativeOperationSwap
{
internal readonly IComponentBuilder[] entityComponents;

public NativeOperationSwap(IComponentBuilder[] descriptorEntitiesToBuild)
{
entityComponents = descriptorEntitiesToBuild;
}
}
}
#endif

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

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

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

+ 31
- 0
Svelto.ECS/Extensions/Unity/DOTS/JobifedEnginesGroup.cs View File

@@ -0,0 +1,31 @@
using Svelto.DataStructures;
using Unity.Jobs;

namespace Svelto.ECS.Extensions.Unity
{
public abstract class JobifedEnginesGroup<Interface>
where Interface : class, IJobifiedEngine
{
protected JobifedEnginesGroup(FasterReadOnlyList<Interface> engines, bool completeEachJob = false)
{
_engines = engines;
_completeEachJob = completeEachJob;
}

public JobHandle Execute(JobHandle combinedHandles)
{
var fasterReadOnlyList = _engines;
for (var index = 0; index < fasterReadOnlyList.Count; index++)
{
var engine = fasterReadOnlyList[index];
combinedHandles = JobHandle.CombineDependencies(combinedHandles, engine.Execute(combinedHandles));
if (_completeEachJob) combinedHandles.Complete();
}

return combinedHandles;
}

readonly FasterReadOnlyList<Interface> _engines;
readonly bool _completeEachJob;
}
}

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

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

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

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

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

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

return _jobHandle;
}

readonly World _world;
}
}
#endif

+ 31
- 0
Svelto.ECS/Extensions/Unity/DOTS/SortedJobifedEnginesGroup.cs View File

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

namespace Svelto.ECS.Extensions.Unity
{
public abstract class SortedJobifedEnginesGroup<Interface, SequenceOrder>
where SequenceOrder : struct, ISequenceOrder where Interface : class, IJobifiedEngine
{
protected SortedJobifedEnginesGroup(FasterReadOnlyList<Interface> engines)
{
_instancedSequence = new Sequence<Interface, SequenceOrder>(engines);
}

public JobHandle Execute(JobHandle combinedHandles)
{
var fasterReadOnlyList = _instancedSequence.items;
for (var index = 0; index < fasterReadOnlyList.Count; index++)
{
var engine = fasterReadOnlyList[index];
combinedHandles = JobHandle.CombineDependencies(combinedHandles, engine.Execute(combinedHandles));
}

return combinedHandles;
}

readonly Sequence<Interface, SequenceOrder> _instancedSequence;
}
}
#endif

+ 23
- 0
Svelto.ECS/Extensions/Unity/DOTS/UECSSveltoEGID.cs View File

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

namespace Svelto.ECS.Extensions.Unity
{
public struct UECSSveltoEGID : IComponentData
{
public EGID egid;
}

public struct UECSSveltoGroupID : ISharedComponentData
{
public readonly uint group;
public UECSSveltoGroupID(uint exclusiveGroup) { @group = exclusiveGroup; }
public static implicit operator ExclusiveGroupStruct(UECSSveltoGroupID group)
{
return new ExclusiveGroupStruct(group.@group);
}
}
}
#endif

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

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

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

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

@@ -1,7 +1,7 @@
#if UNITY_5 || UNITY_5_3_OR_NEWER
using UnityEngine;

namespace Svelto.ECS.Unity
namespace Svelto.ECS.Extensions.Unity
{
public abstract class GenericEntityDescriptorHolder<T>:
MonoBehaviour , IEntityDescriptorHolder


+ 41
- 4
Svelto.ECS/Extensions/Unity/SveltoGUIHelper.cs View File

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

namespace Svelto.ECS.Unity
namespace Svelto.ECS.Extensions.Unity
{
public static class SveltoGUIHelper
{
@@ -30,6 +31,7 @@ namespace Svelto.ECS.Unity
return holder;
}


public static T Create<T>(EGID ID, Transform contextHolder, IEntityFactory factory)
where T : MonoBehaviour, IEntityDescriptorHolder
{
@@ -44,7 +46,7 @@ namespace Svelto.ECS.Unity
return holder;
}

public static EntityStructInitializer CreateWithEntity<T>(EGID ID, Transform contextHolder,
public static EntityComponentInitializer CreateWithEntity<T>(EGID ID, Transform contextHolder,
IEntityFactory factory, out T holder)
where T : MonoBehaviour, IEntityDescriptorHolder
{
@@ -72,7 +74,7 @@ namespace Svelto.ECS.Unity
static uint InternalBuildAll(uint startIndex, IEntityDescriptorHolder descriptorHolder,
IEntityFactory factory, ExclusiveGroup group, IImplementor[] implementors, string groupNamePostfix)
{
ExclusiveGroup.ExclusiveGroupStruct realGroup = group;
ExclusiveGroupStruct realGroup = group;

if (string.IsNullOrEmpty(descriptorHolder.groupName) == false)
{
@@ -90,10 +92,45 @@ namespace Svelto.ECS.Unity

var init = factory.BuildEntity(egid, descriptorHolder.GetDescriptor(), implementors);

init.Init(new EntityHierarchyStruct(group));
init.Init(new EntityHierarchyComponent(group));

return startIndex;
}

/// <summary>
/// Works like CreateAll but only builds entities with holders that have the same group specfied
/// </summary>
/// <param name="startId"></param>
/// <param name="group">The group to match</param>
/// <param name="contextHolder"></param>
/// <param name="factory"></param>
/// <typeparam name="T">EntityDescriptorHolder type</typeparam>
/// <returns>Next available ID</returns>
public static uint CreateAllInMatchingGroup<T>(uint startId, ExclusiveGroup exclusiveGroup,
Transform contextHolder, IEntityFactory factory) where T : MonoBehaviour, IEntityDescriptorHolder
{
var holders = contextHolder.GetComponentsInChildren<T>(true);

foreach (var holder in holders)
{
if (string.IsNullOrEmpty(holder.groupName) == false)
{
var realGroup = ExclusiveGroup.Search(holder.groupName);
if (realGroup != exclusiveGroup)
continue;
}
else
{
continue;
}

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

startId = InternalBuildAll(startId, holder, factory, exclusiveGroup, implementors, null);
}

return startId;
}
}
}
#endif

+ 8
- 6
Svelto.ECS/Extensions/Unity/UnityEntitySubmissionScheduler.cs View File

@@ -1,6 +1,5 @@
#if UNITY_5 || UNITY_5_3_OR_NEWER
using Object = UnityEngine.Object;
using System;
using System.Collections;
using UnityEngine;

@@ -8,7 +7,7 @@ namespace Svelto.ECS.Schedulers.Unity
{
//The EntitySubmissionScheduler has been introduced to make the entity views submission logic platform independent
//You can customize the scheduler if you wish
public class UnityEntitySubmissionScheduler : IEntitySubmissionScheduler
public class UnityEntitiesSubmissionScheduler : IEntitiesSubmissionScheduler
{
class Scheduler : MonoBehaviour
{
@@ -33,16 +32,19 @@ namespace Svelto.ECS.Schedulers.Unity
}

readonly WaitForEndOfFrame _wait = new WaitForEndOfFrame();
readonly IEnumerator _coroutine;
readonly IEnumerator _coroutine;
public EnginesRoot.EntitiesSubmitter onTick;
}
public UnityEntitySubmissionScheduler(string name = "ECSScheduler") { _name = name; }
public UnityEntitiesSubmissionScheduler(string name = "ECSScheduler") { _name = name; }

public void Dispose()
{
Object.Destroy(_scheduler.gameObject);
if (_scheduler != null && _scheduler.gameObject != null)
{
Object.Destroy(_scheduler.gameObject);
}
}
public EnginesRoot.EntitiesSubmitter onTick
@@ -59,7 +61,7 @@ namespace Svelto.ECS.Schedulers.Unity
}
}

Scheduler _scheduler;
Scheduler _scheduler;
readonly string _name;
}
}

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

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

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

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

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

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

@@ -1,104 +1,104 @@
namespace Svelto.ECS
{
public abstract class GenericEntityDescriptor<T> : IEntityDescriptor where T : struct, IEntityStruct
public abstract class GenericEntityDescriptor<T> : IEntityDescriptor where T : struct, IEntityComponent
{
static readonly IEntityBuilder[] _entityBuilders;
static GenericEntityDescriptor() { _entityBuilders = new IEntityBuilder[] {new EntityBuilder<T>()}; }
static readonly IComponentBuilder[] _entityBuilders;
static GenericEntityDescriptor() { _entityBuilders = new IComponentBuilder[] {new ComponentBuilder<T>()}; }

public IEntityBuilder[] entitiesToBuild => _entityBuilders;
public IComponentBuilder[] componentsToBuild => _entityBuilders;
}

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

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

public IEntityBuilder[] entitiesToBuild => _entityBuilders;
public IComponentBuilder[] componentsToBuild => _entityBuilders;
}

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

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

public IEntityBuilder[] entitiesToBuild => _entityBuilders;
public IComponentBuilder[] componentsToBuild => _entityBuilders;
}

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

static GenericEntityDescriptor()
{
_entityBuilders = new IEntityBuilder[]
_entityBuilders = new IComponentBuilder[]
{
new EntityBuilder<T>(),
new EntityBuilder<U>(),
new EntityBuilder<V>(),
new EntityBuilder<W>()
new ComponentBuilder<T>(),
new ComponentBuilder<U>(),
new ComponentBuilder<V>(),
new ComponentBuilder<W>()
};
}

public IEntityBuilder[] entitiesToBuild => _entityBuilders;
public IComponentBuilder[] componentsToBuild => _entityBuilders;
}

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

static GenericEntityDescriptor()
{
_entityBuilders = new IEntityBuilder[]
_entityBuilders = new IComponentBuilder[]
{
new EntityBuilder<T>(),
new EntityBuilder<U>(),
new EntityBuilder<V>(),
new EntityBuilder<W>(),
new EntityBuilder<X>()
new ComponentBuilder<T>(),
new ComponentBuilder<U>(),
new ComponentBuilder<V>(),
new ComponentBuilder<W>(),
new ComponentBuilder<X>()
};
}

public IEntityBuilder[] entitiesToBuild => _entityBuilders;
public IComponentBuilder[] componentsToBuild => _entityBuilders;
}

public abstract class GenericEntityDescriptor<T, U, V, W, X, Y> : IEntityDescriptor
where T : struct, IEntityStruct where U : struct, IEntityStruct where V : struct, IEntityStruct
where W : struct, IEntityStruct where X : struct, IEntityStruct where Y : struct, IEntityStruct
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 IEntityBuilder[] _entityBuilders;
static readonly IComponentBuilder[] _entityBuilders;

static GenericEntityDescriptor()
{
_entityBuilders = new IEntityBuilder[]
_entityBuilders = new IComponentBuilder[]
{
new EntityBuilder<T>(),
new EntityBuilder<U>(),
new EntityBuilder<V>(),
new EntityBuilder<W>(),
new EntityBuilder<X>(),
new EntityBuilder<Y>()
new ComponentBuilder<T>(),
new ComponentBuilder<U>(),
new ComponentBuilder<V>(),
new ComponentBuilder<W>(),
new ComponentBuilder<X>(),
new ComponentBuilder<Y>()
};
}

public IEntityBuilder[] entitiesToBuild => _entityBuilders;
public IComponentBuilder[] componentsToBuild => _entityBuilders;
}
}

+ 9
- 6
Svelto.ECS/GenericentityStreamConsumerFactory.cs View File

@@ -9,12 +9,14 @@ namespace Svelto.ECS
_enginesRoot = new WeakReference<EnginesRoot>(weakReference);
}

public Consumer<T> GenerateConsumer<T>(string name, uint capacity) where T : unmanaged, IEntityStruct
public Consumer<T> GenerateConsumer<T>(string name, uint capacity)
where T : unmanaged, IEntityComponent
{
return _enginesRoot.Target.GenerateConsumer<T>(name, capacity);
}

public Consumer<T> GenerateConsumer<T>(ExclusiveGroup group, string name, uint capacity) where T : unmanaged, IEntityStruct
public Consumer<T> GenerateConsumer<T>(ExclusiveGroup group, string name, uint capacity)
where T : unmanaged, IEntityComponent
{
return _enginesRoot.Target.GenerateConsumer<T>(group, name, capacity);
}
@@ -23,11 +25,12 @@ namespace Svelto.ECS
//engines of other enginesRoot
readonly WeakReference<EnginesRoot> _enginesRoot;
}
public interface IEntityStreamConsumerFactory
{
Consumer<T> GenerateConsumer<T>(string name, uint capacity) where T : unmanaged, IEntityStruct;
Consumer<T> GenerateConsumer<T>(ExclusiveGroup group, string name, uint capacity)
where T : unmanaged, IEntityStruct;
Consumer<T> GenerateConsumer<T>(string name, uint capacity) where T : unmanaged, IEntityComponent;

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

+ 60
- 0
Svelto.ECS/GlobalTypeID.cs View File

@@ -0,0 +1,60 @@
#if UNITY_ECS
using System.Threading;
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);
}

static GlobalTypeID()
{
value = 0;
}

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

class Filler<T>: IFiller where T : struct, IEntityComponent
{
void IFiller.FillFromByteArray(EntityComponentInitializer init, NativeBag buffer)
{
var component = buffer.Dequeue<T>();

init.Init(component);
}
}

static class EntityComponentIDMap
{
static readonly FasterList<IFiller> TYPE_IDS = new FasterList<IFiller>();

internal static void Register<T>(IFiller entityBuilder) where T : struct, IEntityComponent
{
var location = EntityComponentID<T>.ID.Data = GlobalTypeID.NextID<T>();
TYPE_IDS.Add(location, entityBuilder);
}
internal static IFiller GetTypeFromID(uint typeId)
{
return TYPE_IDS[typeId];
}
}
}
#endif

+ 112
- 0
Svelto.ECS/GroupCompound.cs View File

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

namespace Svelto.ECS
{
public static class GroupCompound<G1, G2, G3>
where G1 : GroupTag<G1> where G2 : GroupTag<G2> where G3 : GroupTag<G3>
{
public static readonly ExclusiveGroupStruct[] Groups;

static GroupCompound()
{
if ((Groups = GroupCompound<G3, G1, G2>.Groups) == null)
if ((Groups = GroupCompound<G2, G3, G1>.Groups) == null)
if ((Groups = GroupCompound<G3, G2, G1>.Groups) == null)
if ((Groups = GroupCompound<G1, G3, G2>.Groups) == null)
if ((Groups = GroupCompound<G2, G1, G3>.Groups) == null)
{
Groups = new 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>"));
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);
GroupTag<G1>.Add(Group);
GroupTag<G2>.Add(Group);
GroupTag<G3>.Add(Group);
}
else
Console.LogDebug(typeof(G1).ToString().FastConcat("-", typeof(G2).ToString(), "-").FastConcat(typeof(G3).ToString(), "-", Groups[0].ToString()));
}

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

public static class GroupCompound<G1, G2> where G1 : GroupTag<G1> where G2 : GroupTag<G2>
{
public static ExclusiveGroupStruct[] Groups;

static GroupCompound()
{
Groups = GroupCompound<G2, G1>.Groups;
if (Groups == null)
{
Groups = new ExclusiveGroupStruct[1];
var Group = new ExclusiveGroup();
Groups[0] = 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);
}
else
Console.LogDebug(typeof(G1).ToString().FastConcat("-", typeof(G2).ToString(), "-", Groups[0].ToString()));
}

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)
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;
Console.LogDebug(typeof(G1).ToString().FastConcat("-", typeof(G2).ToString(), "- Add ", group.ToString()));
}
}

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

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

//Each time a new combination of group tags is found a new group is added.
internal static void Add(ExclusiveGroup @group)
{
for (int i = 0; i < Groups.Length; ++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()));
}
}
}

+ 6
- 0
Svelto.ECS/Hybrid/IEntityViewComponent.cs View File

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


+ 0
- 6
Svelto.ECS/Hybrid/IEntityViewStruct.cs View File

@@ -1,6 +0,0 @@
namespace Svelto.ECS.Hybrid
{
public interface IEntityViewStruct:IEntityStruct, INeedEGID
{}
}


+ 1
- 1
Svelto.ECS/Hybrid/IImplementor.cs View File

@@ -1,4 +1,4 @@
namespace Svelto.ECS.Unity
namespace Svelto.ECS.Hybrid
{
public interface IImplementor
{


Svelto.ECS/IEntityBuilder.cs → Svelto.ECS/IComponentBuilder.cs View File

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

namespace Svelto.ECS
{
public interface IEntityBuilder
public interface IComponentBuilder
{
void BuildEntityAndAddToList(ref ITypeSafeDictionary dictionary, EGID egid,
IEnumerable<object> implementors);
ITypeSafeDictionary Preallocate(ref ITypeSafeDictionary dictionary, uint size);

Type GetEntityType();
Type GetEntityComponentType();
}
}

+ 4
- 190
Svelto.ECS/IEntitiesDB.cs View File

@@ -1,193 +1,7 @@
using System;

namespace Svelto.ECS
{
public delegate void ExecuteOnAllEntitiesAction<T, W>(T[] entities, ExclusiveGroup.ExclusiveGroupStruct group,
uint count, IEntitiesDB db, ref W value);

public interface IEntitiesDB
{
///////////////////////////////////////////////////
// Query entities
// ECS systems are meant to work on a set of Entities. These methods allow to iterate over entity
// structs inside a given group or an array of groups
///////////////////////////////////////////////////

/// <summary>
/// Fast and raw return of entities buffer.
/// </summary>
/// <param name="groupStruct"></param>
/// <param name="count"></param>
/// <typeparam name="T"></typeparam>
/// <returns></returns>
T[] QueryEntities<T>(ExclusiveGroup.ExclusiveGroupStruct groupStruct, out uint count)
where T : struct, IEntityStruct;
(T1[], T2[]) QueryEntities<T1, T2>(ExclusiveGroup.ExclusiveGroupStruct groupStruct, out uint count)
where T1 : struct, IEntityStruct where T2 : struct, IEntityStruct;
(T1[], T2[], T3[]) QueryEntities<T1, T2, T3>(ExclusiveGroup.ExclusiveGroupStruct groupStruct, out uint count)
where T1 : struct, IEntityStruct where T2 : struct, IEntityStruct where T3 : struct, IEntityStruct;

/// <summary>
/// return entities that can be iterated through the EntityCollection iterator
/// </summary>
/// <param name="groupStruct"></param>
/// <typeparam name="T"></typeparam>
/// <returns></returns>
EntityCollection<T> QueryEntities<T>(ExclusiveGroup.ExclusiveGroupStruct groupStruct)
where T : struct, IEntityStruct;
EntityCollection<T1, T2> QueryEntities<T1, T2>(ExclusiveGroup.ExclusiveGroupStruct groupStruct)
where T1 : struct, IEntityStruct where T2 : struct, IEntityStruct;
EntityCollection<T1, T2, T3> QueryEntities<T1, T2, T3>(ExclusiveGroup.ExclusiveGroupStruct groupStruct)
where T1 : struct, IEntityStruct
where T2 : struct, IEntityStruct
where T3 : struct, IEntityStruct;

/// <summary>
/// return entities found in multiple groups, that can be iterated through the EntityCollection iterator
/// This method is useful to write abstracted engines
/// </summary>
/// <typeparam name="T"></typeparam>
/// <returns></returns>
EntityCollections<T> QueryEntities<T>(ExclusiveGroup[] groups) where T : struct, IEntityStruct;
EntityCollections<T1, T2> QueryEntities<T1, T2>(ExclusiveGroup[] groups)
where T1 : struct, IEntityStruct where T2 : struct, IEntityStruct;

///////////////////////////////////////////////////
// Query entities regardless the group
// these methods are necessary to create abstracted engines. Engines that can iterate over entities regardless
// the group
///////////////////////////////////////////////////

/// <summary>
/// Execute an action on ALL the entities regardless the group. This function doesn't guarantee cache
/// friendliness even if just EntityStructs are used. Safety checks are in place,
/// </summary>
/// <param name="action"></param>
/// <typeparam name="T"></typeparam>
void ExecuteOnAllEntities<T>(Action<T[], ExclusiveGroup.ExclusiveGroupStruct, uint, IEntitiesDB> action)
where T : struct, IEntityStruct;

/// <summary>
/// same as above, but can pass some external data to avoid allocations
/// </summary>
/// <param name="value"></param>
/// <param name="action"></param>
/// <typeparam name="T"></typeparam>
/// <typeparam name="W"></typeparam>
void ExecuteOnAllEntities<T, W>(ref W value, ExecuteOnAllEntitiesAction<T, W> action)
where T : struct, IEntityStruct;

void ExecuteOnAllEntities<T, W>(W value, Action<T[], ExclusiveGroup.ExclusiveGroupStruct, uint, IEntitiesDB, W> action)
where T : struct, IEntityStruct;

///////////////////////////////////////////////////
// Query single entities
// ECS systems are meant to work on a set of Entities. Working on a single entity is sometime necessary, hence
// the following methods
// However Because of the double hashing required to identify a specific entity, these function are slower than
// other query methods when used multiple times!
///////////////////////////////////////////////////

/// <summary>
/// QueryUniqueEntity is a contract method that explicitly declare the intention to have just on entity in a
/// specific group, usually used for GUI elements
/// </summary>
/// <param name="group"></param>
/// <typeparam name="T"></typeparam>
/// <returns></returns>
ref T QueryUniqueEntity<T>(ExclusiveGroup.ExclusiveGroupStruct group) where T : struct, IEntityStruct;

/// <summary>
/// return a specific entity by reference.
/// </summary>
/// <param name="entityGid"></param>
/// <typeparam name="T"></typeparam>
/// <returns></returns>
ref T QueryEntity<T>(EGID entityGid) where T : struct, IEntityStruct;
ref T QueryEntity<T>(uint id, ExclusiveGroup.ExclusiveGroupStruct group) where T : struct, IEntityStruct;

/// <summary>
///
///QueryEntitiesAndIndex is useful to optimize cases when multiple entity structs from the same entity must
/// be queried. This is the use case:
///
///ref var ghostPosition = ref entitiesDB.QueryEntitiesAndIndex<PositionEntityStruct>
/// (MockupRenderingGroups.GhostCubeID, out var index)[index];
///ref var ghostScaling = ref entitiesDB.QueryEntities<ScalingEntityStruct>
/// (MockupRenderingGroups.GhostCubeID.groupID, out _)[index];
///ref var ghostRotation = ref entitiesDB.QueryEntities<RotationEntityStruct>
/// (MockupRenderingGroups.GhostCubeID.groupID, out _)[index];
///ref var ghostResource = ref entitiesDB.QueryEntities<GFXPrefabEntityStruct>
/// (MockupRenderingGroups.GhostCubeID.groupID, out _)[index];
///
/// </summary>
/// <param name="entityGid"></param>
/// <param name="index"></param>
/// <typeparam name="T"></typeparam>
/// <returns></returns>
T[] QueryEntitiesAndIndex<T>(EGID entityGid, out uint index) where T : struct, IEntityStruct;
T[] QueryEntitiesAndIndex<T>(uint id, ExclusiveGroup.ExclusiveGroupStruct group, out uint index)
where T : struct, IEntityStruct;

/// <summary>
/// Like QueryEntitiesAndIndex and only way to get an index only if exists
/// </summary>
/// <param name="entityGid"></param>
/// <param name="index"></param>
/// <typeparam name="T"></typeparam>
/// <returns></returns>
bool TryQueryEntitiesAndIndex<T>(EGID entityGid, out uint index, out T[] array) where T : struct, IEntityStruct;
bool TryQueryEntitiesAndIndex
<T>(uint id, ExclusiveGroup.ExclusiveGroupStruct group, out uint index, out T[] array)
where T : struct, IEntityStruct;

/// <summary>
/// this method returns a mapped version of the entity array so that is possible to work on multiple entities
/// inside the group through their EGID. This version skip a level of indirection so it's a bit faster than
/// using QueryEntity multiple times (with different EGIDs).
/// However mapping can be slow so it must be used for not performance critical paths
/// </summary>
/// <typeparam name="T"></typeparam>
/// <returns></returns>
EGIDMapper<T> QueryMappedEntities<T>(ExclusiveGroup.ExclusiveGroupStruct groupStructId)
where T : struct, IEntityStruct;
bool TryQueryMappedEntities<T>(ExclusiveGroup.ExclusiveGroupStruct groupStructId, out EGIDMapper<T> mapper)
where T : struct, IEntityStruct;

///////////////////////////////////////////////////
// Utility methods
///////////////////////////////////////////////////

/// <summary>
/// check if a specific entity exists
/// </summary>
/// <param name="egid"></param>
/// <typeparam name="T"></typeparam>
/// <returns></returns>
bool Exists<T>(EGID egid) where T : struct, IEntityStruct;
bool Exists<T>(uint id, ExclusiveGroup.ExclusiveGroupStruct group) where T : struct, IEntityStruct;
bool Exists(ExclusiveGroup.ExclusiveGroupStruct gid);

/// <summary>
/// know if there is any entity struct in a specific group
/// </summary>
/// <param name="groupStruct"></param>
/// <typeparam name="T"></typeparam>
/// <returns></returns>
bool HasAny<T>(ExclusiveGroup.ExclusiveGroupStruct groupStruct) where T : struct, IEntityStruct;

/// <summary>
/// Count the number of entity structs in a specific group
/// </summary>
/// <param name="groupStruct"></param>
/// <typeparam name="T"></typeparam>
/// <returns></returns>
uint Count<T>(ExclusiveGroup.ExclusiveGroupStruct groupStruct) where T : struct, IEntityStruct;

/// <summary>
/// </summary>
/// <param name="egid"></param>
/// <typeparam name="T"></typeparam>
void PublishEntityChange<T>(EGID egid) where T : unmanaged, IEntityStruct;
}
public delegate void ExecuteOnAllEntitiesAction<T, W>(T[] prefabStruct, ExclusiveGroupStruct group,
uint count, EntitiesDB db, ref W instances);
public delegate void ExecuteOnAllEntitiesAction<T>(T[] entities, ExclusiveGroupStruct group,
uint count, EntitiesDB db);
}

+ 16
- 0
Svelto.ECS/IEntityComponent.cs View File

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

/// <summary>
/// use INeedEGID on an IEntityComponent only if you need the EGID
/// </summary>
public interface INeedEGID
{
//The set is used only for the framework, but it must stay there
EGID ID { get; set; }
}
}

+ 18
- 12
Svelto.ECS/IEntityFactory.cs View File

@@ -1,4 +1,5 @@
using System.Collections.Generic;
using Svelto.ECS.Internal;

namespace Svelto.ECS
{
@@ -25,29 +26,29 @@ namespace Svelto.ECS
/// <typeparam name="T"></typeparam>
/// <param name="groupStructId"></param>
/// <param name="size"></param>
void PreallocateEntitySpace<T>(ExclusiveGroup.ExclusiveGroupStruct groupStructId, uint size)
void PreallocateEntitySpace<T>(ExclusiveGroupStruct groupStructId, uint size)
where T : IEntityDescriptor, new();

/// <summary>
/// The EntityDescriptor doesn't need to be ever instantiated. It just describes the Entity
/// itself in terms of EntityViews to build. The Implementors are passed to fill the
/// references of the EntityViews components. Please read the articles on my blog
/// 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
/// are grouped by groupID to be more efficiently processed inside engines and
/// improve cache locality. Either class entityViews and struct entityViews can be
/// improve cache locality. Either class entityComponents and struct entityComponents can be
/// grouped.
/// </summary>
/// <param name="entityID"></param>
/// <param name="groupStructId"></param>
/// <param name="ed"></param>
/// <param name="implementors"></param>
EntityStructInitializer BuildEntity<T>(uint entityID, ExclusiveGroup.ExclusiveGroupStruct groupStructId,
IEnumerable<object> implementors = null)
EntityComponentInitializer BuildEntity<T>(uint entityID, ExclusiveGroupStruct groupStructId,
IEnumerable<object> implementors = null)
where T : IEntityDescriptor, new();

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

/// <summary>
/// When the type of the entity is not known (this is a special case!) an EntityDescriptorInfo
@@ -57,10 +58,15 @@ namespace Svelto.ECS
/// <param name="entityDescriptor"></param>
/// <param name="implementors"></param>
///
EntityStructInitializer BuildEntity<T>(uint entityID, ExclusiveGroup.ExclusiveGroupStruct groupStructId,
T descriptorEntity, IEnumerable<object> implementors = null) where T : IEntityDescriptor;
EntityComponentInitializer BuildEntity<T>(uint entityID, ExclusiveGroupStruct groupStructId,
T descriptorEntity, IEnumerable<object> implementors = null)
where T : IEntityDescriptor;

EntityStructInitializer BuildEntity<T>(EGID egid, T entityDescriptor, IEnumerable<object> implementors = null)
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();
#endif
}
}
}

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

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

void RemoveGroupAndEntities(ExclusiveGroupStruct groupID);

void SwapEntitiesInGroup<T>(ExclusiveGroupStruct fromGroupID, ExclusiveGroupStruct toGroupID);

void SwapEntityGroup<T>(uint entityID, ExclusiveGroupStruct fromGroupID, ExclusiveGroupStruct toGroupID)
where T : IEntityDescriptor, new();

void SwapEntityGroup<T>(EGID fromID, ExclusiveGroupStruct toGroupID) where T : IEntityDescriptor, new();

void SwapEntityGroup<T>(EGID fromID, ExclusiveGroupStruct toGroupID, ExclusiveGroupStruct mustBeFromGroup)
where T : IEntityDescriptor, new();

void SwapEntityGroup<T>(EGID fromID, EGID toId) where T : IEntityDescriptor, new();
void SwapEntityGroup<T>(EGID fromID, EGID toId, ExclusiveGroup.ExclusiveGroupStruct mustBeFromGroup) where T : IEntityDescriptor, new();

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

+ 0
- 15
Svelto.ECS/IEntityStruct.cs View File

@@ -1,15 +0,0 @@
namespace Svelto.ECS
{
///<summary>EntityStruct MUST implement IEntityStruct</summary>
public interface IEntityStruct
{
}

/// <summary>
/// use INeedEGID on an IEntityStruct only if you need the EGID
/// </summary>
public interface INeedEGID
{
EGID ID { get; set; }
}
}

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

@@ -2,7 +2,7 @@ namespace Svelto.ECS
{
public interface IQueryingEntitiesEngine : IEngine
{
IEntitiesDB entitiesDB { set; }
EntitiesDB entitiesDB { set; }

void Ready();
}

+ 3
- 3
Svelto.ECS/IReactOnAddAndRemove.cs View File

@@ -2,9 +2,9 @@ using Svelto.ECS.Internal;

namespace Svelto.ECS
{
public interface IReactOnAddAndRemove<T> : IReactOnAddAndRemove where T : IEntityStruct
public interface IReactOnAddAndRemove<T> : IReactOnAddAndRemove where T : IEntityComponent
{
void Add(ref T entityView, EGID egid);
void Remove(ref T entityView, EGID egid);
void Add(ref T entityComponent, EGID egid);
void Remove(ref T entityComponent, EGID egid);
}
}

+ 3
- 3
Svelto.ECS/IReactOnSwap.cs View File

@@ -2,11 +2,11 @@ using Svelto.ECS.Internal;

namespace Svelto.ECS
{
public interface IReactOnSwap<T> : IReactOnSwap where T : IEntityStruct
public interface IReactOnSwap<T> : IReactOnSwap where T : IEntityComponent
{
void MovedTo(ref T entityView, ExclusiveGroup.ExclusiveGroupStruct previousGroup, EGID egid);
void MovedTo(ref T entityComponent, ExclusiveGroupStruct previousGroup, EGID egid);
#if SEEMS_UNNECESSARY
void MovedFrom(ref T entityView, EGID egid);
void MovedFrom(ref T entityComponent, EGID egid);
#endif
}
}

+ 22
- 0
Svelto.ECS/LICENSE View File

@@ -0,0 +1,22 @@
The MIT License (MIT)

Copyright (c) 2015 Sebastiano Mandalà

Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:

The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.

THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.


+ 79
- 0
Svelto.ECS/NativeEGIDMapper.cs View File

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

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

public NativeEGIDMapper(ExclusiveGroupStruct groupStructId, NativeFasterDictionaryStruct<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);
}

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);
return true;
}

array = default;
return false;
}

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

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

+ 50
- 0
Svelto.ECS/README.md View File

@@ -0,0 +1,50 @@
Svelto Entity Component System for Unity
=====================================

**Note: The alpha stage of Svelto 2.0 is almost completed, so if you are here to experiment with it, please use the current alpha branch**

Real Entity-Component-System for c# and Unity (it can be adapted for other c# platforms too). Enables to write encapsulated, uncoupled, highly efficient, data oriented, cache friendly, multi-threaded, code without pain.

you can find working examples to learn how to use the framework here:

https://github.com/sebas77/Svelto-ECS-Example (unity)

https://github.com/sebas77/Svelto.ECS.Vanilla.Example (.net core and standard)

I advise to clone the example repositories separately from the framework one, both under the same Unity project Assets folder.

relative article:

http://www.sebaslab.com/svelto-ecs-2-0-almost-production-ready/

http://www.sebaslab.com/ecs-1-0/

If you want to know more about the theory and rationale behind this framework:

http://www.sebaslab.com/ioc-container-for-unity3d-part-1/

http://www.sebaslab.com/ioc-container-for-unity3d-part-2/

http://www.sebaslab.com/the-truth-behind-inversion-of-control-part-i-dependency-injection/

http://www.sebaslab.com/the-truth-behind-inversion-of-control-part-ii-inversion-of-control/

http://www.sebaslab.com/the-truth-behind-inversion-of-control-part-iii-entity-component-systems/

http://www.sebaslab.com/the-truth-behind-inversion-of-control-part-iv-dependency-inversion-principle/

http://www.sebaslab.com/the-truth-behind-inversion-of-control-part-v-drifting-away-from-ioc-containers/

new article on optimizations:

http://www.sebaslab.com/svelto-ecs-svelto-tasks-to-write-data-oriented-cache-friendly-multi-threaded-code-in-unity/

Note: if you ever build something with Svelto.ECS that you can share with the community, please do and let me know. Other coders need more examples.

Copyright (c) Sebastiano Mandalà

Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:

The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.

THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.

+ 0
- 132
Svelto.ECS/Sequencer.cs View File

@@ -1,132 +0,0 @@
using System;
using System.Collections;
using System.Collections.Generic;

namespace Svelto.ECS
{
public class Steps
{
internal readonly Dictionary<IEngine, To> _steps;

public Steps(params Step[] values)
{
_steps = new Dictionary<IEngine, To>();

for (var i = 0; i < values.Length; i++)
_steps.Add(values[i].from, values[i].to);
}
}

public class To
{
public To(IStep engine)
{
this.engine = engine;
}
public To(params IStep[] engines)
{
this.engines = engines;
}

public IStep engine { get; set; }
public IStep[] engines { get; set; }
}

public class To<C>:To, IEnumerable where C : struct, IConvertible
{
internal readonly Dictionary<C, IStep<C>[]> _tos = new Dictionary<C, IStep<C>[]>();

public IEnumerator GetEnumerator()
{
throw new NotImplementedException();
}

public void Add(C condition, params IStep<C>[] engine)
{
_tos[condition] = engine;
}
}

public interface IStep
{
void Step(EGID id);
}
public interface IStep<in C> where C:struct,IConvertible
{
void Step(C condition, EGID id);
}

public struct Step
{
public IEngine from { get; set; }
public To to { get; set; }
}
/// <summary>
/// The sequencer has just one goal: define a complex sequence of engines to call. The sequence is not
/// just "sequential", but can create branches and loops.
/// With the time, I figure out that the majority of the times this class is useful in the rare cases where
/// order execution of the engine is necessary/
/// Using branching is even rarer, but still useful sometimes.
/// I used loops only once.
/// There is the chance that this class will become obsolete over the time, as by design engines should
/// never rely on the order of execution
/// Using this class to let engines from different EnginesRoot communicate will also become obsolete, as
/// better solution will be implemented once I have the time
/// Trying to work out how to initialize this structure can be painful. This is by design as this class must
/// be initialized using the following pattern:
/// instancedSequence.SetSequence(
/// new Steps //list of steps
/// (
/// new Step // first step
/// {
/// from = menuOptionEnablerEngine, //starting engine
/// to = new To<ItemActionsPanelEnableCondition> //targets
/// {
/// {
/// ItemActionsPanelEnableCondition.Enable, //condition 1
/// menuPanelEnablerEngine //targets for condition 1
/// },
/// {
/// ItemActionsPanelEnableCondition.Disable,//condition 2
/// menuPanelEnablerEngine //targets for condition 2
/// }
/// }
/// })
/// );
/// </summary>
public class Sequencer<S> where S: Sequencer<S>, new()
{
public void SetSequence(Steps steps)
{
_steps = steps;
}

public void Next<C>(IEngine engine, C condition, EGID id) where C:struct, IConvertible
{
var branch = condition;
var to = (_steps._steps[engine] as To<C>);
var steps = to._tos[branch];

for (var i = 0; i < steps.Length; i++)
steps[i].Step(condition, id);
}
public void Next(IEngine engine, EGID id)
{
var to = _steps._steps[engine];
var steps = to.engines;
if (steps != null && steps.Length > 1)
for (var i = 0; i < steps.Length; i++)
steps[i].Step(id);
else
to.engine.Step(id);
}

Steps _steps;
}
}

+ 42
- 0
Svelto.ECS/Serialization/ComposedComponentSerializer.cs View File

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

namespace Svelto.ECS.Serialization
{
public class ComposedComponentSerializer<T, X, Y> : IComponentSerializer<T>
where T : unmanaged, IEntityComponent where X : class, IComponentSerializer<T>, new()
where Y : class, IComponentSerializer<T>, new()
{
public ComposedComponentSerializer()
{
_serializers = new IComponentSerializer<T>[2];
_serializers[0] = new X();
_serializers[1] = new Y();
}

public bool Serialize(in T value, ISerializationData serializationData)
{
foreach (IComponentSerializer<T> s in _serializers)
{
serializationData.data.ExpandBy(s.size);
if (s.SerializeSafe(value, serializationData))
return true;
}

throw new Exception($"ComposedComponentSerializer for {typeof(T)} did not serialize any data!");
}

public bool Deserialize(ref T value, ISerializationData serializationData)
{
foreach (IComponentSerializer<T> s in _serializers)
{
if (s.DeserializeSafe(ref value, serializationData))
return true;
}

throw new Exception($"ComposedComponentSerializer for {typeof(T)} did not deserialize any data!");
}

public uint size => 0;
IComponentSerializer<T>[] _serializers;
}
}

+ 0
- 43
Svelto.ECS/Serialization/ComposedSerializer.cs View File

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

namespace Svelto.ECS.Serialization
{
public class ComposedSerializer<T, X, Y> : ISerializer<T>
where T : unmanaged, IEntityStruct
where X : class, ISerializer<T>, new()
where Y : class, ISerializer<T>, new()
{
public ComposedSerializer()
{
_serializers = new ISerializer<T>[2];
_serializers[0] = new X();
_serializers[1] = new Y();
}

public bool Serialize(in T value, ISerializationData serializationData)
{
foreach (ISerializer<T> s in _serializers)
{
serializationData.data.ExpandBy(s.size);
if (s.SerializeSafe(value, serializationData))
return true;
}

throw new Exception($"ComposedSerializer for {typeof(T)} did not serialize any data!");
}

public bool Deserialize(ref T value, ISerializationData serializationData)
{
foreach (ISerializer<T> s in _serializers)
{
if (s.DeserializeSafe(ref value, serializationData))
return true;
}

throw new Exception($"ComposedSerializer for {typeof(T)} did not deserialize any data!");
}

public uint size => 0;
ISerializer<T>[] _serializers;
}
}

+ 12
- 13
Svelto.ECS/Serialization/DefaultSerializer.cs View File

@@ -1,30 +1,28 @@
using Svelto.Common;

namespace Svelto.ECS.Serialization
{
public class DefaultSerializer<T> : ISerializer<T> where T : unmanaged, IEntityStruct
public class DefaultSerializer<T> : IComponentSerializer<T> where T : unmanaged, IEntityComponent
{
public static readonly uint SIZEOFT = SerializableEntityBuilder<T>.SIZE;

public uint size => SIZEOFT;
static readonly uint SIZEOFT = SerializableComponentBuilder<T>.SIZE;

static DefaultSerializer()
{
var _type = typeof(T);

foreach (var field in _type.GetFields())
{
if (field.FieldType.ContainsCustomAttribute(typeof(DoNotSerializeAttribute)) && field.IsPrivate == false)
if (field.FieldType.ContainsCustomAttribute(typeof(DoNotSerializeAttribute)) &&
field.IsPrivate == false)
throw new ECSException("field cannot be serialised ".FastConcat(_type.FullName));
}

if (_type.GetProperties().Length > (EntityBuilder<T>.HAS_EGID ? 1 : 0))
if (_type.GetProperties().Length > (ComponentBuilder<T>.HAS_EGID ? 1 : 0))
throw new ECSException("serializable entity struct must be property less ".FastConcat(_type.FullName));
}

public uint size => SIZEOFT;

public bool Serialize(in T value, ISerializationData serializationData)
{
DefaultSerializerUtils.CopyToByteArray(value, serializationData.data.ToArrayFast(), serializationData.dataPos);
DefaultSerializerUtils.CopyToByteArray(value, serializationData.data.ToArrayFast(out _),
serializationData.dataPos);

serializationData.dataPos += SIZEOFT;

@@ -33,11 +31,12 @@ namespace Svelto.ECS.Serialization

public bool Deserialize(ref T value, ISerializationData serializationData)
{
value = DefaultSerializerUtils.CopyFromByteArray<T>(serializationData.data.ToArrayFast(), serializationData.dataPos);
value = DefaultSerializerUtils.CopyFromByteArray<T>(serializationData.data.ToArrayFast(out _),
serializationData.dataPos);

serializationData.dataPos += SIZEOFT;

return true;
}
}
}
}

+ 19
- 3
Svelto.ECS/Serialization/DefaultSerializerUtils.cs View File

@@ -3,8 +3,16 @@ using Svelto.ECS;

public static class DefaultSerializerUtils
{
public static unsafe void CopyToByteArray<T>(in T src, byte[] data, uint offsetDst) where T : unmanaged, IEntityStruct
public static unsafe void CopyToByteArray<T>(in T src, byte[] data, uint offsetDst) where T : unmanaged, IEntityComponent
{
#if DEBUG && !PROFILE_SVELTO
if (data.Length - offsetDst < sizeof(T))
{
throw new IndexOutOfRangeException(
$"Data out of bounds when copying struct {typeof(T).GetType().Name}. data.Length: {data.Length}, offsetDst: {offsetDst}");
}
#endif

fixed (void* dstPtr = data)
{
void* dstOffsetPtr;
@@ -21,9 +29,17 @@ public static class DefaultSerializerUtils
}
}

public static unsafe T CopyFromByteArray<T>(byte[] data, uint offsetSrc) where T : unmanaged, IEntityStruct
public static unsafe T CopyFromByteArray<T>(byte[] data, uint offsetSrc) where T : unmanaged, IEntityComponent
{
T dst = new T();
T dst = default;

#if DEBUG && !PROFILE_SVELTO
if (data.Length - offsetSrc < sizeof(T))
{
throw new IndexOutOfRangeException(
$"Data out of bounds when copying struct {dst.GetType().Name}. data.Length: {data.Length}, offsetSrc: {offsetSrc}");
}
#endif

void* dstPtr = &dst;
fixed (void* srcPtr = data)


+ 22
- 15
Svelto.ECS/Serialization/DefaultVersioningFactory.cs View File

@@ -4,25 +4,32 @@ namespace Svelto.ECS.Serialization
{
public class DefaultVersioningFactory<T> : IDeserializationFactory where T : IEntityDescriptor, new()
{
public EntityStructInitializer BuildDeserializedEntity(EGID egid,
ISerializationData serializationData,
ISerializableEntityDescriptor entityDescriptor,
SerializationType serializationType,
IEntitySerialization entitySerialization)
readonly IEntityFactory _factory;
readonly IEnumerable<object> _implementors;

public DefaultVersioningFactory(IEntityFactory factory)
{
_factory = factory;
}

public DefaultVersioningFactory(IEntityFactory factory, IEnumerable<object> implementors)
{
_factory = factory;
_implementors = implementors;
}

public EntityComponentInitializer BuildDeserializedEntity(EGID egid,
ISerializationData serializationData,
ISerializableEntityDescriptor entityDescriptor,
int serializationType,
IEntitySerialization entitySerialization)
{
var initializer = _factory.BuildEntity<T>(egid, _implementors);
entitySerialization.DeserializeEntityStructs(serializationData, entityDescriptor, ref initializer, serializationType);

entitySerialization.DeserializeEntityComponents(serializationData, entityDescriptor, ref initializer,
serializationType);

return initializer;
}
public DefaultVersioningFactory(IEntityFactory factory) { _factory = factory; }
public DefaultVersioningFactory(IEntityFactory factory, IEnumerable<object> implementors) { _factory = factory;
_implementors = implementors;
}
readonly IEntityFactory _factory;
readonly IEnumerable<object> _implementors;
}
}

+ 1
- 1
Svelto.ECS/Serialization/DontSerialize.cs View File

@@ -1,6 +1,6 @@
namespace Svelto.ECS.Serialization
{
public class DontSerialize<T> : ISerializer<T> where T : unmanaged, IEntityStruct
public class DontSerialize<T> : IComponentSerializer<T> where T : unmanaged, IEntityComponent
{
public uint size => 0;



+ 52
- 44
Svelto.ECS/Serialization/EnginesRoot.GenericEntitySerialization.cs View File

@@ -1,19 +1,8 @@
using System;
using Svelto.Common;
using Svelto.ECS.Internal;
using Svelto.ECS.Serialization;

namespace Svelto.ECS
{
//todo: this should not be at framework level
public enum SerializationType
{
Network,
Storage,

Length
}

public partial class EnginesRoot
{
readonly bool _isDeserializationOnly;
@@ -21,33 +10,33 @@ namespace Svelto.ECS
sealed class EntitySerialization : IEntitySerialization
{
public void SerializeEntity(EGID egid, ISerializationData serializationData,
SerializationType serializationType)
int serializationType)
{
var entitiesDb = _enginesRoot._entitiesDB;

//needs to retrieve the meta data associated with the entity
ref var serializableEntityStruct = ref entitiesDb.QueryEntity<SerializableEntityStruct>(egid);
uint descriptorHash = serializableEntityStruct.descriptorHash;
ref var serializableEntityComponent = ref entitiesDb.QueryEntity<SerializableEntityComponent>(egid);
uint descriptorHash = serializableEntityComponent.descriptorHash;

SerializationDescriptorMap serializationDescriptorMap = _enginesRoot.serializationDescriptorMap;
var entityDescriptor = serializationDescriptorMap.GetDescriptorFromHash(descriptorHash);
var entityStructsToSerialise = entityDescriptor.entitiesToSerialize;
var entityComponentsToSerialise = entityDescriptor.entitiesToSerialize;

var header =
new SerializableEntityHeader(descriptorHash, egid, (byte) entityStructsToSerialise.Length);
new SerializableEntityHeader(descriptorHash, egid, (byte) entityComponentsToSerialise.Length);
header.Copy(serializationData);

for (int index = 0; index < entityStructsToSerialise.Length; index++)
for (int index = 0; index < entityComponentsToSerialise.Length; index++)
{
var entityBuilder = entityStructsToSerialise[index];
var entityBuilder = entityComponentsToSerialise[index];

serializationData.BeginNextEntityStruct();
SerializeEntityStruct(egid, entityBuilder, serializationData, serializationType);
serializationData.BeginNextEntityComponent();
SerializeEntityComponent(egid, entityBuilder, serializationData, serializationType);
}
}

public EntityStructInitializer DeserializeNewEntity(EGID egid, ISerializationData serializationData,
SerializationType serializationType)
public EntityComponentInitializer DeserializeNewEntity(EGID egid, ISerializationData serializationData,
int serializationType)
{
//todo: SerializableEntityHeader may be needed to be customizable
var serializableEntityHeader = new SerializableEntityHeader(serializationData);
@@ -58,14 +47,14 @@ namespace Svelto.ECS
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.entitiesToBuild);
_enginesRoot._isDeserializationOnly ? entityDescriptor.entitiesToSerialize
: entityDescriptor.componentsToBuild);

DeserializeEntityStructs(serializationData, entityDescriptor, ref initializer, serializationType);
DeserializeEntityComponents(serializationData, entityDescriptor, ref initializer, serializationType);

return initializer;
}
@@ -75,7 +64,7 @@ namespace Svelto.ECS
this);
}

public void DeserializeEntity(ISerializationData serializationData, SerializationType serializationType)
public void DeserializeEntity(ISerializationData serializationData, int serializationType)
{
var serializableEntityHeader = new SerializableEntityHeader(serializationData);

@@ -85,38 +74,57 @@ namespace Svelto.ECS
}

public void DeserializeEntity(EGID egid, ISerializationData serializationData,
SerializationType serializationType)
int serializationType)
{
var serializableEntityHeader = new SerializableEntityHeader(serializationData);

DeserializeEntityInternal(serializationData, egid, serializableEntityHeader, serializationType);
}

public void DeserializeEntityStructs(ISerializationData serializationData,
public void DeserializeEntityComponents(ISerializationData serializationData,
ISerializableEntityDescriptor entityDescriptor,
ref EntityStructInitializer initializer, SerializationType serializationType)
ref EntityComponentInitializer initializer, int serializationType)
{
foreach (var serializableEntityBuilder in entityDescriptor.entitiesToSerialize)
{
serializationData.BeginNextEntityStruct();
serializationData.BeginNextEntityComponent();
serializableEntityBuilder.Deserialize(serializationData, initializer, serializationType);
}
}

public T DeserializeEntityComponent<T>(ISerializationData serializationData,
ISerializableEntityDescriptor entityDescriptor, int serializationType)
where T : unmanaged, IEntityComponent
{
var readPos = serializationData.dataPos;
T entityComponent = default;
foreach (var serializableEntityBuilder in entityDescriptor.entitiesToSerialize)
{
if (serializableEntityBuilder is SerializableComponentBuilder<T> entityBuilder)
{
entityBuilder.Deserialize(serializationData, ref entityComponent, serializationType);
}
break;
}

serializationData.dataPos = readPos;
return entityComponent;
}

public void DeserializeEntityToSwap(EGID localEgid, EGID toEgid)
{
EntitiesDB entitiesDb = _enginesRoot._entitiesDB;
ref var serializableEntityStruct = ref entitiesDb.QueryEntity<SerializableEntityStruct>(localEgid);
ref var serializableEntityComponent = ref entitiesDb.QueryEntity<SerializableEntityComponent>(localEgid);

SerializationDescriptorMap serializationDescriptorMap = _enginesRoot.serializationDescriptorMap;
uint descriptorHash = serializableEntityStruct.descriptorHash;
uint descriptorHash = serializableEntityComponent.descriptorHash;
var entityDescriptor = serializationDescriptorMap.GetDescriptorFromHash(descriptorHash);

var entitySubmitOperation = new EntitySubmitOperation(
EntitySubmitOperationType.Swap,
localEgid,
toEgid,
entityDescriptor.entitiesToBuild);
entityDescriptor.componentsToBuild);

_enginesRoot.CheckRemoveEntityID(localEgid);
_enginesRoot.CheckAddEntityID(toEgid);
@@ -127,8 +135,8 @@ namespace Svelto.ECS
public void DeserializeEntityToDelete(EGID egid)
{
EntitiesDB entitiesDB = _enginesRoot._entitiesDB;
ref var serializableEntityStruct = ref entitiesDB.QueryEntity<SerializableEntityStruct>(egid);
uint descriptorHash = serializableEntityStruct.descriptorHash;
ref var serializableEntityComponent = ref entitiesDB.QueryEntity<SerializableEntityComponent>(egid);
uint descriptorHash = serializableEntityComponent.descriptorHash;

SerializationDescriptorMap serializationDescriptorMap = _enginesRoot.serializationDescriptorMap;
var entityDescriptor = serializationDescriptorMap.GetDescriptorFromHash(descriptorHash);
@@ -139,7 +147,7 @@ namespace Svelto.ECS
EntitySubmitOperationType.Remove,
egid,
egid,
entityDescriptor.entitiesToBuild);
entityDescriptor.componentsToBuild);

_enginesRoot.QueueEntitySubmitOperation(entitySubmitOperation);
}
@@ -156,22 +164,22 @@ namespace Svelto.ECS
_enginesRoot = enginesRoot;
}

void SerializeEntityStruct(EGID entityGID, ISerializableEntityBuilder entityBuilder,
ISerializationData serializationData, SerializationType serializationType)
void SerializeEntityComponent(EGID entityGID, ISerializableComponentBuilder componentBuilder,
ISerializationData serializationData, int serializationType)
{
uint groupId = entityGID.groupID;
Type entityType = entityBuilder.GetEntityType();
Type entityType = componentBuilder.GetEntityComponentType();
if (!_enginesRoot._entitiesDB.UnsafeQueryEntityDictionary(groupId, entityType,
out var safeDictionary))
{
throw new Exception("Entity Serialization failed");
}

entityBuilder.Serialize(entityGID.entityID, safeDictionary, serializationData, serializationType);
componentBuilder.Serialize(entityGID.entityID, safeDictionary, serializationData, serializationType);
}

void DeserializeEntityInternal(ISerializationData serializationData, EGID egid,
SerializableEntityHeader serializableEntityHeader, SerializationType serializationType)
SerializableEntityHeader serializableEntityHeader, int serializationType)
{
SerializationDescriptorMap descriptorMap = _enginesRoot.serializationDescriptorMap;
var entityDescriptor = descriptorMap.GetDescriptorFromHash(serializableEntityHeader.descriptorHash);
@@ -179,9 +187,9 @@ namespace Svelto.ECS
foreach (var serializableEntityBuilder in entityDescriptor.entitiesToSerialize)
{
_enginesRoot._entitiesDB.UnsafeQueryEntityDictionary(egid.groupID,
serializableEntityBuilder.GetEntityType(), out var safeDictionary);
serializableEntityBuilder.GetEntityComponentType(), out var safeDictionary);

serializationData.BeginNextEntityStruct();
serializationData.BeginNextEntityComponent();
serializableEntityBuilder.Deserialize(egid.entityID, safeDictionary, serializationData,
serializationType);
}


+ 6
- 8
Svelto.ECS/Serialization/EnginesRoot.SerializableEntityHeader.cs View File

@@ -1,5 +1,3 @@
using Svelto.DataStructures;

namespace Svelto.ECS
{
public partial class EnginesRoot
@@ -7,13 +5,13 @@ namespace Svelto.ECS
struct SerializableEntityHeader
{
public readonly uint descriptorHash;
public readonly byte entityStructsCount;
public readonly byte entityComponentsCount;

const uint SIZE = 4 + 4 + 4 + 1;

internal SerializableEntityHeader(uint descriptorHash_, EGID egid_, byte entityStructsCount_)
internal SerializableEntityHeader(uint descriptorHash_, EGID egid_, byte entityComponentsCount_)
{
entityStructsCount = entityStructsCount_;
entityComponentsCount = entityComponentsCount_;
descriptorHash = descriptorHash_;
egid = egid_;
}
@@ -38,9 +36,9 @@ namespace Svelto.ECS
| serializationData.data[serializationData.dataPos++] << 16
| serializationData.data[serializationData.dataPos++] << 24);

entityStructsCount = serializationData.data[serializationData.dataPos++];
entityComponentsCount = serializationData.data[serializationData.dataPos++];

egid = new EGID(entityID, new ExclusiveGroup.ExclusiveGroupStruct(groupID));
egid = new EGID(entityID, new ExclusiveGroupStruct(groupID));
}

internal void Copy(ISerializationData serializationData)
@@ -67,7 +65,7 @@ namespace Svelto.ECS
serializationData.data[serializationData.dataPos++] = (byte) ((groupID >> 16) & 0xff);
serializationData.data[serializationData.dataPos++] = (byte) ((groupID >> 24) & 0xff);

serializationData.data[serializationData.dataPos++] = entityStructsCount;
serializationData.data[serializationData.dataPos++] = entityComponentsCount;
}

internal readonly EGID egid; //this can't be used safely!


+ 25
- 11
Svelto.ECS/Serialization/EntitiesDB.DescriptorMap.cs View File

@@ -1,6 +1,7 @@
using System;
using System.Collections.Generic;
using System.Reflection;
using Svelto.Common;
using Svelto.ECS.Serialization;

namespace Svelto.ECS
@@ -10,28 +11,40 @@ namespace Svelto.ECS
sealed class SerializationDescriptorMap
{
/// <summary>
/// Use reflection to register all the ISerializableEntityDescriptor to be used for serialization
/// Here we want to register all the EntityDescriptors that need to be serialized for network play.
///
/// Remember! This needs to in sync across different clients and server as the values are serialized across
/// the network also want this to not change so we can save to a DB
/// </summary>
internal SerializationDescriptorMap()
{
_descriptors = new Dictionary<uint, ISerializableEntityDescriptor>();
_factories = new Dictionary<uint, IDeserializationFactory>();

Assembly[] assemblies = AppDomain.CurrentDomain.GetAssemblies();
foreach (Assembly assembly in assemblies)
using (new StandardProfiler("Assemblies Scan"))
{
foreach (Type type in GetTypesSafe(assembly))
Assembly[] assemblies = AppDomain.CurrentDomain.GetAssemblies();
// Assembly executingAssembly = Assembly.GetExecutingAssembly();

foreach (Assembly assembly in assemblies)
{
if (type != null && type.IsClass && type.IsAbstract == false && type.BaseType != null && type.BaseType.IsGenericType &&
type.BaseType.GetGenericTypeDefinition() == typeof(SerializableEntityDescriptor<>))
// if (assembly.GetReferencedAssemblies().Contains(executingAssembly.GetName()))
{
var descriptor = Activator.CreateInstance(type) as ISerializableEntityDescriptor;
foreach (Type type in GetTypesSafe(assembly))
{
if (type != null && type.IsClass && type.IsAbstract == false && type.BaseType != null
&& type.BaseType.IsGenericType && type.BaseType.GetGenericTypeDefinition()
== typeof(SerializableEntityDescriptor<>))
{
var descriptor = Activator.CreateInstance(type) as ISerializableEntityDescriptor;

RegisterEntityDescriptor(descriptor);
RegisterEntityDescriptor(descriptor);
}
}
}
}
}
}
}

static IEnumerable<Type> GetTypesSafe(Assembly assembly)
{
@@ -56,7 +69,7 @@ namespace Svelto.ECS

uint descriptorHash = descriptor.hash;

#if DEBUG && !PROFILER
#if DEBUG && !PROFILE_SVELTO
if (_descriptors.ContainsKey(descriptorHash))
{
throw new Exception($"Hash Collision of '{descriptor.GetType()}' against " +
@@ -69,7 +82,7 @@ namespace Svelto.ECS

public ISerializableEntityDescriptor GetDescriptorFromHash(uint descriptorID)
{
#if DEBUG && !PROFILER
#if DEBUG && !PROFILE_SVELTO
DBC.ECS.Check.Require(_descriptors.ContainsKey(descriptorID),
$"Could not find descriptor with ID '{descriptorID}'!");
#endif
@@ -88,6 +101,7 @@ namespace Svelto.ECS
_factories.Add(SerializationEntityDescriptorTemplate<Descriptor>.hash, deserializationFactory);
}


readonly Dictionary<uint, ISerializableEntityDescriptor> _descriptors;
readonly Dictionary<uint, IDeserializationFactory> _factories;
}


+ 13
- 0
Svelto.ECS/Serialization/IComponentSerializer.cs View File

@@ -0,0 +1,13 @@
#if DEBUG && !PROFILE_SVELTO
#endif

namespace Svelto.ECS.Serialization
{
public interface IComponentSerializer<T> where T : unmanaged, IEntityComponent
{
bool Serialize(in T value, ISerializationData serializationData);
bool Deserialize(ref T value, ISerializationData serializationData);

uint size { get; }
}
}

+ 2
- 2
Svelto.ECS/Serialization/IDeserializationFactory.cs View File

@@ -2,8 +2,8 @@ namespace Svelto.ECS.Serialization
{
public interface IDeserializationFactory
{
EntityStructInitializer BuildDeserializedEntity(EGID egid, ISerializationData serializationData,
ISerializableEntityDescriptor entityDescriptor, SerializationType serializationType,
EntityComponentInitializer BuildDeserializedEntity(EGID egid, ISerializationData serializationData,
ISerializableEntityDescriptor entityDescriptor, int serializationType,
IEntitySerialization entitySerialization);
}
}

+ 12
- 8
Svelto.ECS/Serialization/IEntitySerialization.cs View File

@@ -9,7 +9,7 @@ namespace Svelto.ECS.Serialization
/// <param name="serializedData"></param>
/// <param name="serializationType"></param>
/// <returns>Size in bytes of the newly instantiated entity</returns>
void SerializeEntity(EGID egid, ISerializationData serializationData, SerializationType serializationType);
void SerializeEntity(EGID egid, ISerializationData serializationData, int serializationType);

/// <summary>
/// Deserialize a serializationData and copy directly onto the appropriate entities
@@ -17,7 +17,7 @@ namespace Svelto.ECS.Serialization
/// <param name="data"></param>
/// <param name="dataPos"></param>
/// <param name="serializationType"></param>
void DeserializeEntity(ISerializationData serializationData, SerializationType serializationType);
void DeserializeEntity(ISerializationData serializationData, int serializationType);

/// <summary>
/// Deserialize a serializationData and copy directly onto the appropriate entities with explicit EGID
@@ -26,18 +26,18 @@ namespace Svelto.ECS.Serialization
/// <param name="data"></param>
/// <param name="dataPos"></param>
/// <param name="serializationType"></param>
void DeserializeEntity(EGID egid, ISerializationData serializationData, SerializationType serializationType);
void DeserializeEntity(EGID egid, ISerializationData serializationData, int serializationType);
/// <summary>
/// Deserialize a serializationData and copy directly to an previously created EntityStructInitializer
/// Deserialize a serializationData and copy directly to an previously created EntityComponentInitializer
/// </summary>
/// <param name="serializationData"></param>
/// <param name="entityDescriptor"></param>
/// <param name="initializer"></param>
/// <param name="serializationType"></param>
void DeserializeEntityStructs(ISerializationData serializationData,
void DeserializeEntityComponents(ISerializationData serializationData,
ISerializableEntityDescriptor entityDescriptor,
ref EntityStructInitializer initializer, SerializationType serializationType);
ref EntityComponentInitializer initializer, int serializationType);

/// <summary>
/// Contrary to the other Deserialize methods that assume that the entity exists, this method is used to deserialise
@@ -47,8 +47,8 @@ namespace Svelto.ECS.Serialization
/// <param name="serializationData"></param>
/// <param name="serializationType"></param>
/// <returns></returns>
EntityStructInitializer DeserializeNewEntity(EGID egid, ISerializationData serializationData,
SerializationType serializationType);
EntityComponentInitializer DeserializeNewEntity(EGID egid, ISerializationData serializationData,
int serializationType);

/// <summary>
/// Special Entity Swap method that works without knowing the EntityDescriptor to swap
@@ -65,5 +65,9 @@ namespace Svelto.ECS.Serialization

void RegisterSerializationFactory<T>(IDeserializationFactory deserializationFactory)
where T : ISerializableEntityDescriptor, new();

T DeserializeEntityComponent<T>(ISerializationData serializationData,
ISerializableEntityDescriptor entityDescriptor, int serializationType)
where T : unmanaged, IEntityComponent;
}
}

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

Loading…
Cancel
Save