From df8e6dd21ce3e8049705b3fa204cf960d92ad768 Mon Sep 17 00:00:00 2001 From: sebas77 Date: Sun, 19 Jul 2020 16:08:00 +0100 Subject: [PATCH] Update Svelto.ECS 3.0 --- Svelto.Common | 2 +- Svelto.ECS/.github/FUNDING.yml | 3 - Svelto.ECS/.gitignore | 3 - Svelto.ECS/.gitmodules | 3 - Svelto.ECS/CheckEntityUtilities.cs | 52 +- ...lds.cs => ComponentBuilder.CheckFields.cs} | 10 +- Svelto.ECS/ComponentBuilder.cs | 46 +- Svelto.ECS/DataStructures/AtomicNativeBags.cs | 16 +- .../DataStructures/FastTypeSafeDictionary.cs | 5 - .../DataStructures/ITypeSafeDictionary.cs | 15 +- Svelto.ECS/DataStructures/NativeBag.cs | 38 +- .../DataStructures/NativeDynamicArray.cs | 218 +++++-- .../NativeDynamicArrayUnityExtension.cs | 23 + Svelto.ECS/DataStructures/SharedNativeInt.cs | 2 +- Svelto.ECS/DataStructures/SharedNativeUInt.cs | 70 --- .../DataStructures/TypeSafeDictionary.cs | 565 ++++++++++++------ .../TypeSafeDictionaryUtilities.cs | 8 - Svelto.ECS/DataStructures/UnsafeArray.cs | 47 +- Svelto.ECS/DataStructures/UnsafeBlob.cs | 222 ++++--- Svelto.ECS/Debugger/ExclusiveGroupDebugger.cs | 69 +++ Svelto.ECS/DynamicEntityDescriptor.cs | 12 +- Svelto.ECS/ECSResources/ECSResources.cs | 4 + Svelto.ECS/ECSResources/ECSString.cs | 58 +- Svelto.ECS/EGID.cs | 3 +- Svelto.ECS/EGIDMapper.cs | 47 +- ...EnginesRoot.DoubleBufferedEntitiesToAdd.cs | 148 +++++ .../EnginesRoot.DoubleBufferedEntityViews.cs | 93 --- Svelto.ECS/EnginesRoot.Engines.cs | 99 +-- Svelto.ECS/EnginesRoot.Entities.cs | 281 +++++---- .../EnginesRoot.GenericEntityFactory.cs | 30 +- .../EnginesRoot.GenericEntityFunctions.cs | 29 +- Svelto.ECS/EnginesRoot.Submission.cs | 22 +- Svelto.ECS/EntitiesDB.FindGroups.cs | 126 ++++ Svelto.ECS/EntitiesDB.cs | 297 ++++----- Svelto.ECS/EntityCollection.cs | 480 +++++---------- Svelto.ECS/EntityCollections.cs | 266 +++++++++ Svelto.ECS/EntityComponentInitializer.cs | 4 +- Svelto.ECS/EntityFactory.cs | 12 +- Svelto.ECS/EntityHierarchyStruct.cs | 11 + Svelto.ECS/EntityInfoView.cs | 2 +- Svelto.ECS/EntityNotFoundException.cs | 2 +- Svelto.ECS/EntityStream.cs | 19 +- Svelto.ECS/EntitySubmissionScheduler.cs | 2 + Svelto.ECS/EntityViewUtility.cs | 46 +- Svelto.ECS/ExclusiveGroup.cs | 18 +- Svelto.ECS/ExclusiveGroupStruct.cs | 8 +- Svelto.ECS/Extensions/ProcessorCount.cs | 4 +- ...psEnumerable.cs => AllGroupsEnumerable.cs} | 63 +- .../Svelto/EntityCollectionExtension.cs | 162 +++++ .../Extensions/Svelto/EntityDBExtensions.cs | 136 +++-- .../Extensions/Svelto/EntityDBExtensionsB.cs | 69 +++ .../Extensions/Svelto/GroupsEnumerable.cs | 249 ++++++++ .../Svelto/NativeGroupsEnumerable.cs | 229 ------- .../Extensions/Svelto/SortedEnginesGroup.cs | 86 +++ .../DOTS/CopySveltoToUECSEnginesGroup.cs | 30 - .../Unity/DOTS/EnginesRoot.NativeOperation.cs | 151 ++--- .../Extensions/Unity/DOTS/IJobifiedEngine.cs | 9 - .../Unity/DOTS/JobifedEnginesGroup.cs | 88 ++- .../Unity/DOTS/JobifiedSveltoEngines.cs | 2 +- .../Extensions/Unity/DOTS/NativeEGIDMapper.cs | 83 +++ .../Unity/DOTS/NativeEGIDMultiMapper.cs | 43 ++ .../DOTS/NativeEntityComponentInitializer.cs | 26 + .../Unity/DOTS/NativeEntityFactory.cs | 42 ++ .../Unity/DOTS/NativeEntityRemove.cs | 26 + .../Extensions/Unity/DOTS/NativeEntitySwap.cs | 33 + .../Unity/DOTS/PureUECSSystemsGroup.cs | 2 + .../Unity/DOTS/SortedJobifedEnginesGroup.cs | 58 +- .../Unity/DOTS/SyncSveltoToUECSGroup.cs | 39 ++ .../Unity/DOTS/UnityEntityDBExtensions.cs | 92 ++- .../Extensions/Unity/EGIDHolderImplementor.cs | 32 + .../Extensions/Unity/SveltoGUIHelper.cs | 66 +- .../Unity/UnityEntitySubmissionScheduler.cs | 40 +- Svelto.ECS/GenericEntityDescriptor.cs | 36 +- .../GenericentityStreamConsumerFactory.cs | 4 +- Svelto.ECS/GlobalTypeID.cs | 100 +--- Svelto.ECS/GroupCompound.cs | 108 ++-- Svelto.ECS/IEntitiesDB.cs | 6 +- Svelto.ECS/IEntityFactory.cs | 11 +- Svelto.ECS/IEntityFunctions.cs | 6 +- Svelto.ECS/IReactOnSwap.cs | 3 - Svelto.ECS/NamedExclusiveGroup.cs | 23 + Svelto.ECS/NativeEGIDMapper.cs | 79 --- Svelto.ECS/QueryGroups.cs | 67 ++- Svelto.ECS/Serialization/DefaultSerializer.cs | 7 +- .../Serialization/DefaultVersioningFactory.cs | 27 +- .../EnginesRoot.GenericEntitySerialization.cs | 41 +- .../Serialization/EntitiesDB.DescriptorMap.cs | 13 +- Svelto.ECS/Serialization/HashNameAttribute.cs | 2 +- .../Serialization/IDeserializationFactory.cs | 7 +- .../ISerializableEntityDescriptor.cs | 3 + Svelto.ECS/Serialization/PartialSerializer.cs | 8 +- .../SerializableComponentBuilder.cs | 8 +- .../SerializableEntityDescriptor.cs | 37 +- Svelto.ECS/Serialization/SerializerExt.cs | 6 +- Svelto.ECS/SetEGIDWithoutBoxing.cs | 17 +- .../SimpleEntitiesSubmissionScheduler.cs | 5 +- Svelto.ECS/Svelto.ECS.asmdef | 16 + Svelto.ECS/Svelto.ECS.csproj | 2 +- Svelto.ECS/TupleRef.cs | 58 ++ Svelto.ECS/TypeSafeDictionaryFactory.cs | 4 +- Svelto.ECS/package.json | 4 +- 101 files changed, 3793 insertions(+), 2311 deletions(-) delete mode 100644 Svelto.ECS/.github/FUNDING.yml delete mode 100644 Svelto.ECS/.gitignore delete mode 100644 Svelto.ECS/.gitmodules rename Svelto.ECS/{EntityBuilder.CheckFields.cs => ComponentBuilder.CheckFields.cs} (94%) create mode 100644 Svelto.ECS/DataStructures/NativeDynamicArrayUnityExtension.cs delete mode 100644 Svelto.ECS/DataStructures/SharedNativeUInt.cs create mode 100644 Svelto.ECS/Debugger/ExclusiveGroupDebugger.cs create mode 100644 Svelto.ECS/EnginesRoot.DoubleBufferedEntitiesToAdd.cs delete mode 100644 Svelto.ECS/EnginesRoot.DoubleBufferedEntityViews.cs create mode 100644 Svelto.ECS/EntitiesDB.FindGroups.cs create mode 100644 Svelto.ECS/EntityCollections.cs create mode 100644 Svelto.ECS/EntityHierarchyStruct.cs rename Svelto.ECS/Extensions/Svelto/{NativeAllGroupsEnumerable.cs => AllGroupsEnumerable.cs} (68%) create mode 100644 Svelto.ECS/Extensions/Svelto/EntityCollectionExtension.cs create mode 100644 Svelto.ECS/Extensions/Svelto/EntityDBExtensionsB.cs create mode 100644 Svelto.ECS/Extensions/Svelto/GroupsEnumerable.cs delete mode 100644 Svelto.ECS/Extensions/Svelto/NativeGroupsEnumerable.cs create mode 100644 Svelto.ECS/Extensions/Svelto/SortedEnginesGroup.cs delete mode 100644 Svelto.ECS/Extensions/Unity/DOTS/CopySveltoToUECSEnginesGroup.cs delete mode 100644 Svelto.ECS/Extensions/Unity/DOTS/IJobifiedEngine.cs create mode 100644 Svelto.ECS/Extensions/Unity/DOTS/NativeEGIDMapper.cs create mode 100644 Svelto.ECS/Extensions/Unity/DOTS/NativeEGIDMultiMapper.cs create mode 100644 Svelto.ECS/Extensions/Unity/DOTS/NativeEntityComponentInitializer.cs create mode 100644 Svelto.ECS/Extensions/Unity/DOTS/NativeEntityFactory.cs create mode 100644 Svelto.ECS/Extensions/Unity/DOTS/NativeEntityRemove.cs create mode 100644 Svelto.ECS/Extensions/Unity/DOTS/NativeEntitySwap.cs create mode 100644 Svelto.ECS/Extensions/Unity/DOTS/SyncSveltoToUECSGroup.cs create mode 100644 Svelto.ECS/Extensions/Unity/EGIDHolderImplementor.cs create mode 100644 Svelto.ECS/NamedExclusiveGroup.cs delete mode 100644 Svelto.ECS/NativeEGIDMapper.cs create mode 100644 Svelto.ECS/TupleRef.cs diff --git a/Svelto.Common b/Svelto.Common index 800c1a9..7d7aaba 160000 --- a/Svelto.Common +++ b/Svelto.Common @@ -1 +1 @@ -Subproject commit 800c1a9abe35986fabb6562178e27d3b17c34b5c +Subproject commit 7d7aaba893e8aa0a24b8c36b46a8ed805a8f514c diff --git a/Svelto.ECS/.github/FUNDING.yml b/Svelto.ECS/.github/FUNDING.yml deleted file mode 100644 index f6dca80..0000000 --- a/Svelto.ECS/.github/FUNDING.yml +++ /dev/null @@ -1,3 +0,0 @@ -# These are supported funding model platforms - -custom: https://www.paypal.me/smandala diff --git a/Svelto.ECS/.gitignore b/Svelto.ECS/.gitignore deleted file mode 100644 index 7ef0a76..0000000 --- a/Svelto.ECS/.gitignore +++ /dev/null @@ -1,3 +0,0 @@ -/obj -/bin/Release/netstandard2.0 -/bin/Debug/netstandard2.0 diff --git a/Svelto.ECS/.gitmodules b/Svelto.ECS/.gitmodules deleted file mode 100644 index cd05d00..0000000 --- a/Svelto.ECS/.gitmodules +++ /dev/null @@ -1,3 +0,0 @@ -[submodule "Svelto.Common"] - path = Svelto.Common - url = https://github.com/sebas77/Svelto.Common diff --git a/Svelto.ECS/CheckEntityUtilities.cs b/Svelto.ECS/CheckEntityUtilities.cs index e85326b..a002893 100644 --- a/Svelto.ECS/CheckEntityUtilities.cs +++ b/Svelto.ECS/CheckEntityUtilities.cs @@ -1,29 +1,30 @@ #if DEBUG && !PROFILE_SVELTO +using System; using System.Collections.Generic; using Svelto.DataStructures; #else +using System; using System.Diagnostics; #endif namespace Svelto.ECS { /// - /// Note: this check doesn't catch the case when an add and remove is done on the same entity before the next - /// submission. Two operations on the same entity are not allowed between submissions. + /// Note: this check doesn't catch the case when an add and remove is done on the same entity before the nextI am + /// submission. Two operations on the same entity are not allowed between submissions. /// public partial class EnginesRoot { -#if DEBUG && !PROFILE_SVELTO - void CheckRemoveEntityID(EGID egid) +#if DEBUG && !PROFILE_SVELTO + void CheckRemoveEntityID(EGID egid, Type entityComponent, string caller = "") { - // Console.LogError("removed".FastConcat(egid.ToString())); if (_idCheckers.TryGetValue(egid.groupID, out var hash)) { if (hash.Contains(egid.entityID) == false) throw new ECSException("Entity with not found ID is about to be removed: id: " - .FastConcat(egid.entityID) - .FastConcat(" groupid: ") - .FastConcat(egid.groupID)); + .FastConcat(" caller: ", caller, " ") + .FastConcat(egid.entityID).FastConcat(" groupid: ").FastConcat(egid.groupID.ToName()) + .FastConcat(" type: ").FastConcat(entityComponent != null ? entityComponent.Name : "not available")); hash.Remove(egid.entityID); @@ -33,46 +34,39 @@ namespace Svelto.ECS else { throw new ECSException("Entity with not found ID is about to be removed: id: " - .FastConcat(egid.entityID) - .FastConcat(" groupid: ") - .FastConcat(egid.groupID)); + .FastConcat(" caller: ", caller, " ") + .FastConcat(egid.entityID).FastConcat(" groupid: ").FastConcat(egid.groupID.ToName()) + .FastConcat(" type: ").FastConcat(entityComponent != null ? entityComponent.Name : "not available")); } } - void CheckAddEntityID(EGID egid) + void CheckAddEntityID(EGID egid, Type entityComponent, string caller = "") { // Console.LogError("added ".FastConcat(egid.ToString())); - + if (_idCheckers.TryGetValue(egid.groupID, out var hash) == false) hash = _idCheckers[egid.groupID] = new HashSet(); else - { if (hash.Contains(egid.entityID)) throw new ECSException("Entity with used ID is about to be built: '" - .FastConcat("' id: '") - .FastConcat(egid.entityID) - .FastConcat("' groupid: '") - .FastConcat(egid.groupID) - .FastConcat("'")); - } - + .FastConcat("' id: '").FastConcat(egid.entityID).FastConcat("' groupid: '") + .FastConcat(egid.groupID.ToName()).FastConcat(" ", entityComponent != null ? entityComponent.Name : "not available") + .FastConcat("'")); + hash.Add(egid.entityID); } - - void RemoveGroupID(ExclusiveGroupStruct groupID) - { - _idCheckers.Remove(groupID); - } + + void RemoveGroupID(ExclusiveGroupStruct groupID) { _idCheckers.Remove(groupID); } readonly FasterDictionary> _idCheckers = new FasterDictionary>(); #else [Conditional("_CHECKS_DISABLED")] - void CheckRemoveEntityID(EGID egid) + void CheckRemoveEntityID(EGID egid, Type entityComponent, string caller = "") { } [Conditional("_CHECKS_DISABLED")] - void CheckAddEntityID(EGID egid) + void CheckAddEntityID(EGID egid, Type entityComponen, string caller = "") { } @@ -82,4 +76,4 @@ namespace Svelto.ECS } #endif } -} +} \ No newline at end of file diff --git a/Svelto.ECS/EntityBuilder.CheckFields.cs b/Svelto.ECS/ComponentBuilder.CheckFields.cs similarity index 94% rename from Svelto.ECS/EntityBuilder.CheckFields.cs rename to Svelto.ECS/ComponentBuilder.CheckFields.cs index 0895f16..6ff8372 100644 --- a/Svelto.ECS/EntityBuilder.CheckFields.cs +++ b/Svelto.ECS/ComponentBuilder.CheckFields.cs @@ -7,7 +7,7 @@ using System.Reflection; namespace Svelto.ECS { - internal static class EntityBuilderUtilities + internal static class ComponentBuilderUtilities { const string MSG = "Entity Structs field and Entity View Struct components must hold value types."; @@ -48,16 +48,16 @@ namespace Svelto.ECS if (fields.Length < 1) { - ProcessError("Entity View Structs must hold only entity components interfaces.", entityComponentType); + ProcessError("No valid fields found in Entity View Struct", entityComponentType); } for (int i = fields.Length - 1; i >= 0; --i) { FieldInfo fieldInfo = fields[i]; - if (fieldInfo.FieldType.IsInterfaceEx() == false) + if (fieldInfo.FieldType.IsInterfaceEx() == false && fieldInfo.FieldType.IsValueTypeEx() == false) { - ProcessError("Entity View Structs must hold only entity components interfaces.", + ProcessError("Entity View Structs must hold only public interfaces or value type fields.", entityComponentType); } @@ -120,7 +120,7 @@ namespace Svelto.ECS static readonly Type SERIALIZABLE_ENTITY_STRUCT = typeof(SerializableEntityComponent); static readonly Type STRINGTYPE = typeof(string); - internal static readonly Type ENTITY_STRUCT_INFO_VIEW = typeof(EntityInfoComponentView); + internal static readonly Type ENTITY_STRUCT_INFO_VIEW = typeof(EntityInfoViewComponent); } public class EntityComponentException : Exception diff --git a/Svelto.ECS/ComponentBuilder.cs b/Svelto.ECS/ComponentBuilder.cs index 5b58344..f36c1c8 100644 --- a/Svelto.ECS/ComponentBuilder.cs +++ b/Svelto.ECS/ComponentBuilder.cs @@ -1,6 +1,7 @@ using System; using System.Collections.Generic; using System.Reflection; +using Svelto.Common; using Svelto.DataStructures; using Svelto.ECS.Hybrid; using Svelto.ECS.Internal; @@ -13,11 +14,6 @@ namespace Svelto.ECS public ComponentBuilder() { _initializer = DEFAULT_IT; - - EntityBuilderUtilities.CheckFields(ENTITY_COMPONENT_TYPE, IS_ENTITY_VIEW_COMPONENT); - - if (IS_ENTITY_VIEW_COMPONENT) - EntityViewComponentCache.InitCache(); } public ComponentBuilder(in T initializer) : this() @@ -36,8 +32,6 @@ namespace Svelto.ECS T entityComponent = default; if (IS_ENTITY_VIEW_COMPONENT) { - DBC.ECS.Check.Require(implementors != null, - $"Implementors not found while building an EntityComponent `{typeof(T)}`"); DBC.ECS.Check.Require(castedDic.ContainsKey(egid.entityID) == false, $"building an entity with already used entity id! id: '{(ulong) egid}', {ENTITY_COMPONENT_NAME}"); @@ -82,21 +76,35 @@ namespace Svelto.ECS IS_ENTITY_VIEW_COMPONENT = typeof(IEntityViewComponent).IsAssignableFrom(ENTITY_COMPONENT_TYPE); HAS_EGID = typeof(INeedEGID).IsAssignableFrom(ENTITY_COMPONENT_TYPE); ENTITY_COMPONENT_NAME = ENTITY_COMPONENT_TYPE.ToString(); -#if UNITY_ECS - EntityComponentIDMap.Register(new Filler()); -#endif + var IS_UNMANAGED = ENTITY_COMPONENT_TYPE.IsUnmanaged(); + + if (IS_UNMANAGED) + EntityComponentIDMap.Register(new Filler()); + SetEGIDWithoutBoxing.Warmup(); + + ComponentBuilderUtilities.CheckFields(ENTITY_COMPONENT_TYPE, IS_ENTITY_VIEW_COMPONENT); + + if (IS_ENTITY_VIEW_COMPONENT) + EntityViewComponentCache.InitCache(); + else + { + if (ENTITY_COMPONENT_TYPE != ComponentBuilderUtilities.ENTITY_STRUCT_INFO_VIEW && ENTITY_COMPONENT_TYPE.IsUnmanaged() == false) + throw new Exception($"Entity Component check failed, unexpected struct type (must be unmanaged) {ENTITY_COMPONENT_TYPE}"); + } } + readonly T _initializer; internal static readonly Type ENTITY_COMPONENT_TYPE; - public static readonly bool HAS_EGID; + public static readonly bool HAS_EGID; + internal static readonly bool IS_ENTITY_VIEW_COMPONENT; - static readonly T DEFAULT_IT; - static readonly bool IS_ENTITY_VIEW_COMPONENT; - static readonly string ENTITY_COMPONENT_NAME; + static readonly T DEFAULT_IT; + static readonly string ENTITY_COMPONENT_NAME; + static class EntityViewComponentCache { internal static readonly FasterList>> cachedFields; @@ -116,11 +124,13 @@ namespace Svelto.ECS for (var i = fields.Length - 1; i >= 0; --i) { var field = fields[i]; - DBC.ECS.Check.Require(field.FieldType.IsInterface == true, "Entity View Components must hold only public interfaces"); - var setter = FastInvoke.MakeSetter(field); + if (field.FieldType.IsInterface == true) + { + var setter = FastInvoke.MakeSetter(field); - //for each interface, cache the setter for this type - cachedFields.Add(new KeyValuePair>(field.FieldType, setter)); + //for each interface, cache the setter for this type + cachedFields.Add(new KeyValuePair>(field.FieldType, setter)); + } } cachedTypes = new Dictionary(); diff --git a/Svelto.ECS/DataStructures/AtomicNativeBags.cs b/Svelto.ECS/DataStructures/AtomicNativeBags.cs index 0a99862..c858617 100644 --- a/Svelto.ECS/DataStructures/AtomicNativeBags.cs +++ b/Svelto.ECS/DataStructures/AtomicNativeBags.cs @@ -12,13 +12,13 @@ namespace Svelto.ECS.DataStructures.Unity public unsafe struct AtomicNativeBags:IDisposable { public const int DefaultThreadIndex = -1; - public const int MinThreadIndex = DefaultThreadIndex; + const int MinThreadIndex = DefaultThreadIndex; -#if UNITY_ECS +#if UNITY_COLLECTIONS [global::Unity.Collections.LowLevel.Unsafe.NativeDisableUnsafePtrRestriction] #endif - NativeBag* _data; - public readonly Allocator Allocator; + readonly NativeBag* _data; + readonly Allocator _allocator; readonly uint _threadsCount; [MethodImpl(MethodImplOptions.AggressiveInlining)] @@ -26,20 +26,20 @@ namespace Svelto.ECS.DataStructures.Unity public AtomicNativeBags(Common.Allocator allocator, uint threadsCount) { - Allocator = allocator; + _allocator = allocator; _threadsCount = threadsCount; var bufferSize = MemoryUtilities.SizeOf(); var bufferCount = _threadsCount; var allocationSize = bufferSize * bufferCount; - var ptr = (byte*)MemoryUtilities.Alloc((uint) allocationSize, allocator); + var ptr = (byte*)MemoryUtilities.Alloc((uint) allocationSize, allocator); MemoryUtilities.MemClear((IntPtr) ptr, (uint) allocationSize); for (int i = 0; i < bufferCount; i++) { var bufferPtr = (NativeBag*)(ptr + bufferSize * i); - var buffer = new NativeBag((uint) i, allocator); + var buffer = new NativeBag(allocator); MemoryUtilities.CopyStructureToPtr(ref buffer, (IntPtr) bufferPtr); } @@ -60,7 +60,7 @@ namespace Svelto.ECS.DataStructures.Unity { GetBuffer(i).Dispose(); } - MemoryUtilities.Free((IntPtr) _data, Allocator); + MemoryUtilities.Free((IntPtr) _data, _allocator); } public void Clear() diff --git a/Svelto.ECS/DataStructures/FastTypeSafeDictionary.cs b/Svelto.ECS/DataStructures/FastTypeSafeDictionary.cs index 19cb75d..dc55efc 100644 --- a/Svelto.ECS/DataStructures/FastTypeSafeDictionary.cs +++ b/Svelto.ECS/DataStructures/FastTypeSafeDictionary.cs @@ -264,11 +264,6 @@ namespace Svelto.ECS.Internal get => _implementation.unsafeValues; } - public object GenerateSentinel() - { - throw new NotImplementedException(); - } - public SetDictionary implementation => _implementation; [MethodImpl(MethodImplOptions.AggressiveInlining)] diff --git a/Svelto.ECS/DataStructures/ITypeSafeDictionary.cs b/Svelto.ECS/DataStructures/ITypeSafeDictionary.cs index d8a744b..f111a12 100644 --- a/Svelto.ECS/DataStructures/ITypeSafeDictionary.cs +++ b/Svelto.ECS/DataStructures/ITypeSafeDictionary.cs @@ -7,34 +7,27 @@ namespace Svelto.ECS.Internal public interface ITypeSafeDictionary : ITypeSafeDictionary where TValue : IEntityComponent { void Add(uint egidEntityId, in TValue entityComponent); - ref TValue GetValueByRef(uint key); ref TValue this[uint idEntityId] { get; } bool TryGetValue(uint entityId, out TValue item); ref TValue GetOrCreate(uint idEntityId); - TValue[] GetValuesArray(out uint count); - TValue[] unsafeValues { get; } - object GenerateSentinel(); + IBuffer GetValues(out uint count); + ref TValue GetDirectValueByRef(uint key); } - public interface ITypeSafeDictionary + public interface ITypeSafeDictionary:IDisposable { - uint Count { get; } + uint count { get; } ITypeSafeDictionary Create(); void AddEntitiesToEngines(FasterDictionary, FasterList> entityComponentEnginesDb, ITypeSafeDictionary realDic, ExclusiveGroupStruct @group, in PlatformProfiler profiler); - void RemoveEntitiesFromEngines(FasterDictionary, FasterList> entityComponentEnginesDB, in PlatformProfiler profiler, ExclusiveGroupStruct @group); - void AddEntitiesFromDictionary(ITypeSafeDictionary entitiesToSubmit, uint groupId); - void MoveEntityFromEngines(EGID fromEntityGid, EGID? toEntityID, ITypeSafeDictionary toGroup, FasterDictionary, FasterList> engines, in PlatformProfiler profiler); - void AddEntityToDictionary(EGID fromEntityGid, EGID toEntityID, ITypeSafeDictionary toGroup); - void RemoveEntityFromDictionary(EGID fromEntityGid); void SetCapacity(uint size); diff --git a/Svelto.ECS/DataStructures/NativeBag.cs b/Svelto.ECS/DataStructures/NativeBag.cs index 6db1021..e8a5ab9 100644 --- a/Svelto.ECS/DataStructures/NativeBag.cs +++ b/Svelto.ECS/DataStructures/NativeBag.cs @@ -12,12 +12,12 @@ namespace Svelto.ECS.DataStructures /// is done. /// You can reserve a position in the queue to update it later. /// The datastructure is a struct and it's "copyable" - /// I eventually decided to call it NativeBag and not NativeRingBuffer because it can also be used as + /// I eventually decided to call it NativeBag and not NativeBag because it can also be used as /// a preallocated memory pool where any kind of T can be stored as long as T is unmanaged /// public struct NativeBag : IDisposable { -#if UNITY_ECS +#if UNITY_COLLECTIONS [global::Unity.Collections.LowLevel.Unsafe.NativeDisableUnsafePtrRestriction] #endif unsafe UnsafeBlob* _queue; @@ -73,31 +73,11 @@ namespace Svelto.ECS.DataStructures unsafe { var sizeOf = MemoryUtilities.SizeOf(); - var listData = (UnsafeBlob*) MemoryUtilities.Alloc((uint) sizeOf, allocator); + var listData = (UnsafeBlob*) MemoryUtilities.Alloc((uint) sizeOf, allocator); //clear to nullify the pointers MemoryUtilities.MemClear((IntPtr) listData, (uint) sizeOf); listData->allocator = allocator; -#if DEBUG && !PROFILE_SVELTO - listData->id = 0xDEADBEEF; -#endif - _queue = listData; - } - } - - public NativeBag(uint bufferID, Allocator allocator) - { - unsafe - { - var sizeOf = MemoryUtilities.SizeOf(); - var listData = (UnsafeBlob*) MemoryUtilities.Alloc((uint) sizeOf, allocator); - - //clear to nullify the pointers - MemoryUtilities.MemClear((IntPtr) listData, (uint) sizeOf); - listData->allocator = allocator; -#if DEBUG && !PROFILE_SVELTO - listData->id = bufferID; -#endif _queue = listData; } } @@ -113,7 +93,7 @@ namespace Svelto.ECS.DataStructures } [MethodImpl(MethodImplOptions.AggressiveInlining)] - public ref T ReserveEnqueue(out UnsafeArrayIndex index) where T : unmanaged + public ref T ReserveEnqueue(out UnsafeArrayIndex index) where T : struct { unsafe { @@ -123,14 +103,14 @@ namespace Svelto.ECS.DataStructures #endif var sizeOf = MemoryUtilities.SizeOf(); if (_queue->space - sizeOf < 0) - _queue->Realloc((uint) ((_queue->capacity + sizeOf) * 1.5f)); + _queue->Realloc((uint) ((_queue->capacity + sizeOf) * 2.0f)); return ref _queue->Reserve(out index); } } [MethodImpl(MethodImplOptions.AggressiveInlining)] - public void Enqueue(in T item) where T : unmanaged + public void Enqueue(in T item) where T : struct { unsafe { @@ -140,7 +120,7 @@ namespace Svelto.ECS.DataStructures #endif var sizeOf = MemoryUtilities.SizeOf(); if (_queue->space - sizeOf < 0) - _queue->Realloc((uint) ((_queue->capacity + sizeOf) * 1.5f)); + _queue->Realloc((uint) ((_queue->capacity + MemoryUtilities.Align4((uint) sizeOf)) * 2.0f)); _queue->Write(item); } @@ -159,7 +139,7 @@ namespace Svelto.ECS.DataStructures } } - public T Dequeue() where T : unmanaged + public T Dequeue() where T : struct { unsafe { @@ -167,7 +147,7 @@ namespace Svelto.ECS.DataStructures } } - public ref T AccessReserved(UnsafeArrayIndex reserverIndex) where T : unmanaged + public ref T AccessReserved(UnsafeArrayIndex reserverIndex) where T : struct { unsafe { diff --git a/Svelto.ECS/DataStructures/NativeDynamicArray.cs b/Svelto.ECS/DataStructures/NativeDynamicArray.cs index eefb7d9..83efdd2 100644 --- a/Svelto.ECS/DataStructures/NativeDynamicArray.cs +++ b/Svelto.ECS/DataStructures/NativeDynamicArray.cs @@ -7,8 +7,8 @@ namespace Svelto.ECS.DataStructures { public struct NativeDynamicArray : IDisposable { -#if UNITY_ECS - [global::Unity.Collections.LowLevel.Unsafe.NativeDisableUnsafePtrRestriction] +#if UNITY_COLLECTIONS + [global::Unity.Burst.NoAlias] [global::Unity.Collections.LowLevel.Unsafe.NativeDisableUnsafePtrRestriction] #endif unsafe UnsafeArray* _list; #if DEBUG && !PROFILE_SVELTO @@ -16,7 +16,7 @@ namespace Svelto.ECS.DataStructures #endif [MethodImpl(MethodImplOptions.AggressiveInlining)] - public uint Count() where T:unmanaged + public int Count() where T : struct { unsafe { @@ -26,13 +26,13 @@ namespace Svelto.ECS.DataStructures if (hashType != TypeHash.hash) throw new Exception("NativeDynamicArray: not excepted type used"); -#endif - return (uint) (_list->count / MemoryUtilities.SizeOf()); +#endif + return (_list->count / MemoryUtilities.SizeOf()); } } - + [MethodImpl(MethodImplOptions.AggressiveInlining)] - public uint Capacity() where T:unmanaged + public int Capacity() where T : struct { unsafe { @@ -42,12 +42,12 @@ namespace Svelto.ECS.DataStructures if (hashType != TypeHash.hash) throw new Exception("NativeDynamicArray: not excepted type used"); -#endif - return (uint) (_list->capacity / MemoryUtilities.SizeOf()); +#endif + return (_list->capacity / MemoryUtilities.SizeOf()); } } - public static NativeDynamicArray Alloc(Allocator allocator, uint newLength = 0) where T : unmanaged + public static NativeDynamicArray Alloc(Allocator allocator, uint newLength = 0) where T : struct { unsafe { @@ -55,17 +55,16 @@ namespace Svelto.ECS.DataStructures #if DEBUG && !PROFILE_SVELTO rtnStruc.hashType = TypeHash.hash; #endif - var sizeOf = MemoryUtilities.SizeOf(); + var sizeOf = MemoryUtilities.SizeOf(); + + uint pointerSize = (uint) MemoryUtilities.SizeOf(); + UnsafeArray* listData = (UnsafeArray*) MemoryUtilities.Alloc(pointerSize, allocator); - uint pointerSize = (uint) MemoryUtilities.SizeOf(); - UnsafeArray* listData = - (UnsafeArray*) MemoryUtilities.Alloc(pointerSize, allocator); - //clear to nullify the pointers MemoryUtilities.MemClear((IntPtr) listData, pointerSize); listData->allocator = allocator; - listData->Realloc((uint) (newLength * sizeOf)); + listData->Realloc((uint) (newLength * sizeOf)); rtnStruc._list = listData; @@ -74,7 +73,7 @@ namespace Svelto.ECS.DataStructures } [MethodImpl(MethodImplOptions.AggressiveInlining)] - public ref T Get(uint index) where T : unmanaged + public ref T Get(uint index) where T : struct { unsafe { @@ -91,7 +90,7 @@ namespace Svelto.ECS.DataStructures } [MethodImpl(MethodImplOptions.AggressiveInlining)] - public void Set(uint index, in T value) where T : unmanaged + public void Set(uint index, in T value) where T : struct { unsafe { @@ -101,24 +100,24 @@ namespace Svelto.ECS.DataStructures if (hashType != TypeHash.hash) throw new Exception("NativeDynamicArray: not excepted type used"); if (index >= Capacity()) - throw new Exception($"NativeDynamicArray: out of bound access, index {index} count {Count()}"); -#endif + throw new Exception($"NativeDynamicArray: out of bound access, index {index} capacity {Capacity()}"); +#endif _list->Set(index, value); } } public unsafe void Dispose() { -#if DEBUG && !PROFILE_SVELTO +#if DEBUG && !PROFILE_SVELTO if (_list == null) throw new Exception("NativeDynamicArray: null-access"); #endif - _list->Dispose(); - _list = null; + _list->Dispose(); + _list = null; } [MethodImpl(MethodImplOptions.AggressiveInlining)] - public void Add(in T item) where T : unmanaged + public void Add(in T item) where T : struct { unsafe { @@ -129,16 +128,52 @@ namespace Svelto.ECS.DataStructures throw new Exception("NativeDynamicArray: not excepted type used"); #endif var structSize = (uint) MemoryUtilities.SizeOf(); - - if (_list->space - (int)structSize < 0) - _list->Realloc((uint) ((Count() + 1) * structSize * 1.5f)); - + + if (_list->space - (int) structSize < 0) + _list->Realloc((uint) (((uint) ((Count() + 1) * 1.5f) * (float) structSize))); + _list->Add(item); } } - + + public void Grow(uint newCapacity) where T : struct + { + unsafe + { +#if DEBUG && !PROFILE_SVELTO + if (_list == null) + throw new Exception("NativeDynamicArray: null-access"); + if (hashType != TypeHash.hash) + throw new Exception("NativeDynamicArray: not excepted type used"); + if (newCapacity <= Capacity()) + throw new Exception("New capacity must be greater than current one"); +#endif + uint structSize = (uint) MemoryUtilities.SizeOf(); + + uint size = (uint) (newCapacity * structSize); + _list->Realloc((uint) size); + } + } + + public void SetCount(uint count) where T : struct + { + unsafe + { +#if DEBUG && !PROFILE_SVELTO + if (_list == null) + throw new Exception("NativeDynamicArray: null-access"); + if (hashType != TypeHash.hash) + throw new Exception("NativeDynamicArray: not excepted type used"); +#endif + uint structSize = (uint) MemoryUtilities.SizeOf(); + uint size = (uint) (count * structSize); + + _list->SetCountTo((uint) size); + } + } + [MethodImpl(MethodImplOptions.AggressiveInlining)] - public void AddWithoutGrow(in T item) where T : unmanaged + public void AddWithoutGrow(in T item) where T : struct { unsafe { @@ -157,6 +192,29 @@ namespace Svelto.ECS.DataStructures } } + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public void UnorderedRemoveAt(uint index) where T : struct + { + unsafe + { +#if DEBUG && !PROFILE_SVELTO + if (_list == null) + throw new Exception("NativeDynamicArray: null-access"); + if (hashType != TypeHash.hash) + throw new Exception("NativeDynamicArray: not excepted type used"); + if (Count() == 0) + throw new Exception("NativeDynamicArray: empty array invalid operation"); +#endif + var count = Count() - 1; + if (index < count) + { + Set(index, Get((uint) count)); + } + + _list->Pop(); + } + } + [MethodImpl(MethodImplOptions.AggressiveInlining)] public void Clear() { @@ -182,6 +240,21 @@ namespace Svelto.ECS.DataStructures return (T*) _list->ptr; } + public IntPtr ToIntPTR() where T : struct + { + unsafe + { +#if DEBUG && !PROFILE_SVELTO + if (_list == null) + throw new Exception("NativeDynamicArray: null-access"); + if (hashType != TypeHash.hash) + throw new Exception("NativeDynamicArray: not excepted type used"); + +#endif + return (IntPtr) _list->ptr; + } + } + public T[] ToManagedArray() where T : unmanaged { unsafe @@ -193,17 +266,19 @@ namespace Svelto.ECS.DataStructures throw new Exception("NativeDynamicArray: not excepted type used"); #endif - var ret = new T[Count()]; + var count = Count(); + var ret = new T[count]; + var lengthToCopyInBytes = count * MemoryUtilities.SizeOf(); - fixed (void * handle = ret) + fixed (void* handle = ret) { - Buffer.MemoryCopy(_list->ptr, handle, _list->count, _list->count); + Unsafe.CopyBlock(handle, _list->ptr, (uint) lengthToCopyInBytes); } return ret; } } - + public T[] ToManagedArrayUntrimmed() where T : unmanaged { unsafe @@ -213,17 +288,82 @@ namespace Svelto.ECS.DataStructures throw new Exception("NativeDynamicArray: null-access"); if (hashType != TypeHash.hash) throw new Exception("NativeDynamicArray: not excepted type used"); - #endif - var ret = new T[Capacity()]; + var capacity = Capacity(); + var lengthToCopyInBytes = capacity * MemoryUtilities.SizeOf(); + var ret = new T[capacity]; - fixed (void * handle = ret) + fixed (void* handle = ret) { - Buffer.MemoryCopy(_list->ptr, handle, _list->capacity, _list->capacity); + Unsafe.CopyBlock(handle, _list->ptr, (uint) lengthToCopyInBytes); } return ret; } } + + public void RemoveAt(uint index) where T : struct + { + unsafe + { +#if DEBUG && !PROFILE_SVELTO + if (_list == null) + throw new Exception("NativeDynamicArray: null-access"); + if (hashType != TypeHash.hash) + throw new Exception("NativeDynamicArray: not excepted type used"); +#endif + + var sizeOf = MemoryUtilities.SizeOf(); + //Unsafe.CopyBlock may not be memory overlapping safe (memcpy vs memmove) + Buffer.MemoryCopy(_list->ptr + (index + 1) * sizeOf, _list->ptr + index * sizeOf, _list->count + , (uint) ((Count() - (index + 1)) * sizeOf)); + _list->Pop(); + } + } + + public void MemClear() + { + unsafe + { + MemoryUtilities.MemClear((IntPtr) _list->ptr, (uint) _list->capacity); + } + } + } + + public ref struct NativeDynamicArrayCast where T : struct + { + NativeDynamicArray _array; + + public NativeDynamicArrayCast(NativeDynamicArray array) : this() { _array = array; } + + public int count + { + [MethodImpl(MethodImplOptions.AggressiveInlining)] + get => _array.Count(); + } + + public ref T this[int index] + { + [MethodImpl(MethodImplOptions.AggressiveInlining)] + get => ref _array.Get((uint) index); + } + + public ref T this[uint index] + { + [MethodImpl(MethodImplOptions.AggressiveInlining)] + get => ref _array.Get(index); + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public void Add(in T id) { _array.Add(id); } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public void UnorderedRemoveAt(uint index) { _array.UnorderedRemoveAt(index); } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public void RemoveAt(uint index) { _array.RemoveAt(index); } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public void Clear() { _array.Clear(); } } -} +} \ No newline at end of file diff --git a/Svelto.ECS/DataStructures/NativeDynamicArrayUnityExtension.cs b/Svelto.ECS/DataStructures/NativeDynamicArrayUnityExtension.cs new file mode 100644 index 0000000..3688940 --- /dev/null +++ b/Svelto.ECS/DataStructures/NativeDynamicArrayUnityExtension.cs @@ -0,0 +1,23 @@ +#if UNITY_COLLECTIONS +using Unity.Collections; +using Unity.Collections.LowLevel.Unsafe; + +namespace Svelto.ECS.DataStructures +{ + public static class NativeDynamicArrayUnityExtension + { + public static NativeArray ToNativeArray(this NativeDynamicArray array) where T : struct + { + unsafe + { + var nativeArray = NativeArrayUnsafeUtility.ConvertExistingDataToNativeArray( + (void*) array.ToIntPTR(), (int) array.Count(), Allocator.None); +#if ENABLE_UNITY_COLLECTIONS_CHECKS + NativeArrayUnsafeUtility.SetAtomicSafetyHandle(ref nativeArray, AtomicSafetyHandle.Create()); +#endif + return nativeArray; + } + } + } +} +#endif \ No newline at end of file diff --git a/Svelto.ECS/DataStructures/SharedNativeInt.cs b/Svelto.ECS/DataStructures/SharedNativeInt.cs index 1bcecdb..b5269e6 100644 --- a/Svelto.ECS/DataStructures/SharedNativeInt.cs +++ b/Svelto.ECS/DataStructures/SharedNativeInt.cs @@ -6,7 +6,7 @@ namespace Svelto.ECS.DataStructures { public struct SharedNativeInt: IDisposable { -#if UNITY_ECS +#if UNITY_COLLECTIONS [global::Unity.Collections.LowLevel.Unsafe.NativeDisableUnsafePtrRestriction] #endif unsafe int* data; diff --git a/Svelto.ECS/DataStructures/SharedNativeUInt.cs b/Svelto.ECS/DataStructures/SharedNativeUInt.cs deleted file mode 100644 index a6474b3..0000000 --- a/Svelto.ECS/DataStructures/SharedNativeUInt.cs +++ /dev/null @@ -1,70 +0,0 @@ -using System; -using System.Runtime.CompilerServices; -using System.Runtime.InteropServices; -using System.Threading; - -namespace Svelto.ECS.DataStructures -{ - public struct SharedNativeUInt: IDisposable - { -#if UNITY_ECS - [global::Unity.Collections.LowLevel.Unsafe.NativeDisableUnsafePtrRestriction] -#endif - unsafe uint* data; - - public SharedNativeUInt(uint t) - { - unsafe - { - data = (uint*) Marshal.AllocHGlobal(sizeof(uint)); - *data = t; - } - } - - public static implicit operator uint(SharedNativeUInt t) - { - unsafe - { -#if DEBUG && !PROFILE_SVELTO - if (t.data == null) - throw new Exception("using disposed SharedUInt"); -#endif - - return *t.data; - } - } - - public void Dispose() - { - unsafe - { - if (data != null) - { - Marshal.FreeHGlobal((IntPtr) data); - data = null; - } - } - } - - public void Decrement() - { - unsafe - { - int result = Interlocked.Decrement(ref Unsafe.As(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(ref *data)); - } - } - } -} \ No newline at end of file diff --git a/Svelto.ECS/DataStructures/TypeSafeDictionary.cs b/Svelto.ECS/DataStructures/TypeSafeDictionary.cs index 7ce5ef9..2bc30e0 100644 --- a/Svelto.ECS/DataStructures/TypeSafeDictionary.cs +++ b/Svelto.ECS/DataStructures/TypeSafeDictionary.cs @@ -2,202 +2,468 @@ using System.Runtime.CompilerServices; using Svelto.Common; using Svelto.DataStructures; +using Svelto.ECS.Hybrid; namespace Svelto.ECS.Internal { sealed class TypeSafeDictionary : ITypeSafeDictionary where TValue : struct, IEntityComponent { - static readonly Type _type = typeof(TValue); - static readonly string _typeName = _type.Name; - static readonly bool _hasEgid = typeof(INeedEGID).IsAssignableFrom(_type); + static readonly Type _type = typeof(TValue); + static readonly string _typeName = _type.Name; + static readonly bool _hasEgid = typeof(INeedEGID).IsAssignableFrom(_type); + + internal static readonly bool _isUmanaged = + _type.IsUnmanaged() && (typeof(IEntityViewComponent).IsAssignableFrom(_type) == false); + + internal SveltoDictionary>, ManagedStrategy> implMgd; + internal SveltoDictionary>, NativeStrategy> implUnmgd; public TypeSafeDictionary(uint size) { - _implementation = new FasterDictionary(size); + if (_isUmanaged) + implUnmgd = new SveltoDictionary>, NativeStrategy>(size); + else + { + implMgd = new SveltoDictionary>, ManagedStrategy>(size); + } } - public TypeSafeDictionary() + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public void Add(uint egidEntityId, in TValue entityComponent) { - _implementation = new FasterDictionary(1); + if (_isUmanaged) + implUnmgd.Add(egidEntityId, entityComponent); + else + implMgd.Add(egidEntityId, entityComponent); } /// - /// Add entities from external typeSafeDictionary + /// Add entities from external typeSafeDictionary /// /// /// /// - public void AddEntitiesFromDictionary(ITypeSafeDictionary entitiesToSubmit, uint groupId) + public void AddEntitiesFromDictionary(ITypeSafeDictionary entitiesToSubmit, uint groupId) { - var typeSafeDictionary = entitiesToSubmit as TypeSafeDictionary; + if (_isUmanaged) + { + var typeSafeDictionary = (entitiesToSubmit as TypeSafeDictionary).implUnmgd; - foreach (var tuple in typeSafeDictionary) + foreach (var tuple in typeSafeDictionary) + try + { + if (_hasEgid) + SetEGIDWithoutBoxing.SetIDWithoutBoxing( + ref tuple.Value, new EGID(tuple.Key, groupId)); + + implUnmgd.Add(tuple.Key, tuple.Value); + } + catch (Exception e) + { + Console.LogException( + e, "trying to add an EntityComponent with the same ID more than once Entity: ".FastConcat(typeof(TValue).ToString()).FastConcat(", group ").FastConcat(groupId).FastConcat(", id ").FastConcat(tuple.Key)); + + throw; + } + } + else { - try - { - if (_hasEgid) - SetEGIDWithoutBoxing.SetIDWithoutBoxing(ref tuple.Value, new EGID(tuple.Key, groupId)); + var typeSafeDictionary = (entitiesToSubmit as TypeSafeDictionary).implMgd; - _implementation.Add(tuple.Key, tuple.Value); - } - catch (Exception e) - { - Svelto.Console.LogException(e, - "trying to add an EntityComponent with the same ID more than once Entity: " - .FastConcat(typeof(TValue).ToString()).FastConcat(", group ").FastConcat(groupId) - .FastConcat(", id ").FastConcat(tuple.Key)); + foreach (var tuple in typeSafeDictionary) + try + { + if (_hasEgid) + SetEGIDWithoutBoxing.SetIDWithoutBoxing( + ref tuple.Value, new EGID(tuple.Key, groupId)); - throw; - } + implMgd.Add(tuple.Key, tuple.Value); + } + catch (Exception e) + { + Console.LogException( + e, "trying to add an EntityComponent with the same ID more than once Entity: ".FastConcat(typeof(TValue).ToString()).FastConcat(", group ").FastConcat(groupId).FastConcat(", id ").FastConcat(tuple.Key)); + + throw; + } } } - + + public void AddEntitiesToEngines + (FasterDictionary, FasterList> entityComponentEnginesDB + , ITypeSafeDictionary realDic, ExclusiveGroupStruct group, in PlatformProfiler profiler) + { + if (_isUmanaged) + { + var typeSafeDictionary = realDic as ITypeSafeDictionary; + + //this can be optimized, should pass all the entities and not restart the process for each one + foreach (var value in implUnmgd) + AddEntityComponentToEngines(entityComponentEnginesDB, ref typeSafeDictionary[value.Key] + , null, in profiler, new EGID(value.Key, group)); + } + else + { + var typeSafeDictionary = realDic as ITypeSafeDictionary; + + //this can be optimized, should pass all the entities and not restart the process for each one + foreach (var value in implMgd) + AddEntityComponentToEngines(entityComponentEnginesDB, ref typeSafeDictionary[value.Key] + , null, in profiler, new EGID(value.Key, group)); + } + } + public void AddEntityToDictionary(EGID fromEntityGid, EGID toEntityID, ITypeSafeDictionary toGroup) { - var valueIndex = _implementation.GetIndex(fromEntityGid.entityID); + if (_isUmanaged) + { + var valueIndex = implUnmgd.GetIndex(fromEntityGid.entityID); + + DBC.ECS.Check.Require(toGroup != null + , "Invalid To Group"); //todo check this, if it's right merge GetIndex + { + var toGroupCasted = toGroup as ITypeSafeDictionary; + ref var entity = ref implUnmgd.GetDirectValueByRef(valueIndex); + + if (_hasEgid) + SetEGIDWithoutBoxing.SetIDWithoutBoxing(ref entity, toEntityID); - if (toGroup != null) + toGroupCasted.Add(fromEntityGid.entityID, entity); + } + } + else { - var toGroupCasted = toGroup as ITypeSafeDictionary; - ref var entity = ref _implementation.unsafeValues[(int) valueIndex]; + var valueIndex = implMgd.GetIndex(fromEntityGid.entityID); - if (_hasEgid) SetEGIDWithoutBoxing.SetIDWithoutBoxing(ref entity, toEntityID); + DBC.ECS.Check.Require(toGroup != null + , "Invalid To Group"); //todo check this, if it's right merge GetIndex + { + var toGroupCasted = toGroup as ITypeSafeDictionary; + ref var entity = ref implMgd.GetDirectValueByRef(valueIndex); + + if (_hasEgid) + SetEGIDWithoutBoxing.SetIDWithoutBoxing(ref entity, toEntityID); - toGroupCasted.Add(fromEntityGid.entityID, entity); + toGroupCasted.Add(fromEntityGid.entityID, entity); + } } } - public void AddEntitiesToEngines(FasterDictionary, FasterList> entityComponentEnginesDB, - ITypeSafeDictionary realDic, - ExclusiveGroupStruct @group, - in PlatformProfiler profiler) + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public void Clear() { - var typeSafeDictionary = realDic as ITypeSafeDictionary; + if (_isUmanaged) + { + implUnmgd.Clear(); + } + else + { + implMgd.Clear(); + } + } - //this can be optimized, should pass all the entities and not restart the process for each one - foreach (var value in _implementation) - AddEntityComponentToEngines(entityComponentEnginesDB, ref typeSafeDictionary.GetValueByRef(value.Key), null, - in profiler, new EGID(value.Key, group)); + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public void FastClear() + { + if (_isUmanaged) + { + implUnmgd.FastClear(); + } + else + { + implMgd.FastClear(); + } } - public void RemoveEntitiesFromEngines( - FasterDictionary, FasterList> entityComponentEnginesDB, in PlatformProfiler profiler, - ExclusiveGroupStruct @group) + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public bool ContainsKey(uint egidEntityId) { - foreach (var value in _implementation) - RemoveEntityComponentFromEngines(entityComponentEnginesDB, ref _implementation.GetValueByRef(value.Key), null, - in profiler, new EGID(value.Key, group)); + if (_isUmanaged) + { + return implUnmgd.ContainsKey(egidEntityId); + } + else + { + return implMgd.ContainsKey(egidEntityId); + } } [MethodImpl(MethodImplOptions.AggressiveInlining)] - public void FastClear() { _implementation.FastClear(); } + public ITypeSafeDictionary Create() + { + return TypeSafeDictionaryFactory.Create(1); + } [MethodImpl(MethodImplOptions.AggressiveInlining)] - public bool Has(uint key) { return _implementation.ContainsKey(key); } + public uint GetIndex(uint valueEntityId) + { + if (_isUmanaged) + { + return this.implUnmgd.GetIndex(valueEntityId); + } + else + { + return this.implMgd.GetIndex(valueEntityId); + } + } [MethodImpl(MethodImplOptions.AggressiveInlining)] - public void RemoveEntityFromDictionary(EGID fromEntityGid) + public ref TValue GetOrCreate(uint idEntityId) { - _implementation.Remove(fromEntityGid.entityID); + if (_isUmanaged) + { + return ref this.implUnmgd.GetOrCreate(idEntityId); + } + else + { + return ref this.implMgd.GetOrCreate(idEntityId); + } } [MethodImpl(MethodImplOptions.AggressiveInlining)] - public void SetCapacity(uint size) { _implementation.SetCapacity(size); } + public IBuffer GetValues(out uint count) + { + if (_isUmanaged) + { + return this.implUnmgd.GetValues(out count); + } + else + { + return this.implMgd.GetValues(out count); + } + } [MethodImpl(MethodImplOptions.AggressiveInlining)] - public void Trim() { _implementation.Trim(); } + public ref TValue GetDirectValueByRef(uint key) + { + if (_isUmanaged) + { + return ref this.implUnmgd.GetDirectValueByRef(key); + } + else + { + return ref this.implMgd.GetDirectValueByRef(key); + } + } [MethodImpl(MethodImplOptions.AggressiveInlining)] - public void Clear() { _implementation.Clear(); } + public bool Has(uint key) + { + if (_isUmanaged) + { + return this.implUnmgd.ContainsKey(key); + } + else + { + return this.implMgd.ContainsKey(key); + } + } - public void MoveEntityFromEngines(EGID fromEntityGid, - EGID? toEntityID, ITypeSafeDictionary toGroup, - FasterDictionary, FasterList> engines, - in PlatformProfiler profiler) + public void MoveEntityFromEngines + (EGID fromEntityGid, EGID? toEntityID, ITypeSafeDictionary toGroup + , FasterDictionary, FasterList> engines, in PlatformProfiler profiler) { - var valueIndex = _implementation.GetIndex(fromEntityGid.entityID); + if (_isUmanaged) + { + var valueIndex = this.implUnmgd.GetIndex(fromEntityGid.entityID); + + ref var entity = ref this.implUnmgd.GetDirectValueByRef(valueIndex); + + if (toGroup != null) + { + RemoveEntityComponentFromEngines(engines, ref entity, fromEntityGid.groupID, in profiler + , fromEntityGid); - ref var entity = ref _implementation.unsafeValues[(int) valueIndex]; + var toGroupCasted = toGroup as ITypeSafeDictionary; + var previousGroup = fromEntityGid.groupID; - if (toGroup != null) + if (_hasEgid) + SetEGIDWithoutBoxing.SetIDWithoutBoxing(ref entity, toEntityID.Value); + + var index = toGroupCasted.GetIndex(toEntityID.Value.entityID); + + AddEntityComponentToEngines(engines, ref toGroupCasted.GetDirectValueByRef(index), previousGroup + , in profiler, toEntityID.Value); + } + else + { + RemoveEntityComponentFromEngines(engines, ref entity, null, in profiler, fromEntityGid); + } + } + else { - RemoveEntityComponentFromEngines(engines, ref entity, fromEntityGid.groupID, in profiler, fromEntityGid); + var valueIndex = this.implMgd.GetIndex(fromEntityGid.entityID); - var toGroupCasted = toGroup as ITypeSafeDictionary; - var previousGroup = fromEntityGid.groupID; + ref var entity = ref this.implMgd.GetDirectValueByRef(valueIndex); - if (_hasEgid) SetEGIDWithoutBoxing.SetIDWithoutBoxing(ref entity, toEntityID.Value); + if (toGroup != null) + { + RemoveEntityComponentFromEngines(engines, ref entity, fromEntityGid.groupID, in profiler + , fromEntityGid); + + var toGroupCasted = toGroup as ITypeSafeDictionary; + var previousGroup = fromEntityGid.groupID; + + if (_hasEgid) + SetEGIDWithoutBoxing.SetIDWithoutBoxing(ref entity, toEntityID.Value); - var index = toGroupCasted.GetIndex(toEntityID.Value.entityID); + var index = toGroupCasted.GetIndex(toEntityID.Value.entityID); - AddEntityComponentToEngines(engines, ref toGroupCasted.unsafeValues[(int) index], previousGroup, in profiler, - toEntityID.Value); + AddEntityComponentToEngines(engines, ref toGroupCasted.GetDirectValueByRef(index), previousGroup + , in profiler, toEntityID.Value); + } + else + { + RemoveEntityComponentFromEngines(engines, ref entity, null, in profiler, fromEntityGid); + } + } + } + + public void RemoveEntitiesFromEngines(FasterDictionary, FasterList> engines + , in PlatformProfiler profiler, ExclusiveGroupStruct group) + { + if (_isUmanaged) + { + foreach (var value in implUnmgd) + RemoveEntityComponentFromEngines(engines, ref implUnmgd.GetValueByRef(value.Key), null + , in profiler, new EGID(value.Key, group)); } else - RemoveEntityComponentFromEngines(engines, ref entity, null, in profiler, fromEntityGid); + { + foreach (var value in implMgd) + RemoveEntityComponentFromEngines(engines, ref implMgd.GetValueByRef(value.Key), null + , in profiler, new EGID(value.Key, group)); + } } - public uint Count + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public void RemoveEntityFromDictionary(EGID fromEntityGid) { - [MethodImpl(MethodImplOptions.AggressiveInlining)] - get => _implementation.count; + if (_isUmanaged) + { + this.implUnmgd.Remove(fromEntityGid.entityID); + } + else + { + this.implMgd.Remove(fromEntityGid.entityID); + } } [MethodImpl(MethodImplOptions.AggressiveInlining)] - public ITypeSafeDictionary Create() { return new TypeSafeDictionary(); } + public void SetCapacity(uint size) + { + if (_isUmanaged) + { + this.implUnmgd.SetCapacity(size); + } + else + { + this.implMgd.SetCapacity(size); + } + } - void AddEntityComponentToEngines(FasterDictionary, FasterList> entityComponentEnginesDB, - ref TValue entity, - ExclusiveGroupStruct? previousGroup, - in PlatformProfiler profiler, - EGID egid) + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public void Trim() { - //get all the engines linked to TValue - if (!entityComponentEnginesDB.TryGetValue(new RefWrapper(_type), out var entityComponentsEngines)) return; + if (_isUmanaged) + { + this.implUnmgd.Trim(); + } + else + { + this.implMgd.Trim(); + } + } - if (previousGroup == null) + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public bool TryFindIndex(uint entityId, out uint index) + { + if (_isUmanaged) { - for (var i = 0; i < entityComponentsEngines.count; i++) - try - { - using (profiler.Sample(entityComponentsEngines[i], _typeName)) - { - (entityComponentsEngines[i] as IReactOnAddAndRemove).Add(ref entity, egid); - } - } - catch (Exception e) - { - throw new - ECSException("Code crashed inside Add callback ".FastConcat(typeof(TValue).ToString()), e); - } + return implUnmgd.TryFindIndex(entityId, out index); + } + else + { + return implMgd.TryFindIndex(entityId, out index); + } + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public bool TryGetValue(uint entityId, out TValue item) + { + if (_isUmanaged) + { + return this.implUnmgd.TryGetValue(entityId, out item); } else { + return this.implMgd.TryGetValue(entityId, out item); + } + } + + public uint count + { + [MethodImpl(MethodImplOptions.AggressiveInlining)] + get + { + if (_isUmanaged) + { + return this.implUnmgd.count; + } + else + { + return this.implMgd.count; + } + } + } + + public ref TValue this[uint idEntityId] + { + [MethodImpl(MethodImplOptions.AggressiveInlining)] + get + { + if (_isUmanaged) + { + return ref this.implUnmgd.GetValueByRef(idEntityId); + } + else + { + return ref this.implMgd.GetValueByRef(idEntityId); + } + } + } + + static void RemoveEntityComponentFromEngines + (FasterDictionary, FasterList> engines, ref TValue entity, uint? previousGroup + , in PlatformProfiler profiler, EGID egid) + { + if (!engines.TryGetValue(new RefWrapper(_type), out var entityComponentsEngines)) + return; + + if (previousGroup == null) for (var i = 0; i < entityComponentsEngines.count; i++) try { using (profiler.Sample(entityComponentsEngines[i], _typeName)) { - (entityComponentsEngines[i] as IReactOnSwap).MovedTo(ref entity, previousGroup.Value, - egid); + (entityComponentsEngines[i] as IReactOnAddAndRemove).Remove(ref entity, egid); } } - catch (Exception e) + catch { - throw new - ECSException("Code crashed inside MovedTo callback ".FastConcat(typeof(TValue).ToString()), - e); + Svelto.Console.LogError("Code crashed inside Remove callback ".FastConcat(typeof(TValue).ToString())); + + throw; } - } } - static void RemoveEntityComponentFromEngines(FasterDictionary, FasterList> @group, - ref TValue entity, - uint? previousGroup, - in PlatformProfiler profiler, - EGID egid) + void AddEntityComponentToEngines + (FasterDictionary, FasterList> engines, ref TValue entity + , ExclusiveGroupStruct? previousGroup, in PlatformProfiler profiler, EGID egid) { - if (!@group.TryGetValue(new RefWrapper(_type), out var entityComponentsEngines)) return; + //get all the engines linked to TValue + if (!engines.TryGetValue(new RefWrapper(_type), out var entityComponentsEngines)) + return; if (previousGroup == null) { @@ -205,86 +471,45 @@ namespace Svelto.ECS.Internal try { using (profiler.Sample(entityComponentsEngines[i], _typeName)) - (entityComponentsEngines[i] as IReactOnAddAndRemove).Remove(ref entity, egid); + { + (entityComponentsEngines[i] as IReactOnAddAndRemove).Add(ref entity, egid); + } } - catch (Exception e) + catch { - throw new - ECSException("Code crashed inside Remove callback ".FastConcat(typeof(TValue).ToString()), - e); + Svelto.Console.LogError("Code crashed inside Add callback ".FastConcat(typeof(TValue).ToString())); + + throw; } } -#if SEEMS_UNNECESSARY else { - for (var i = 0; i < entityComponentsEngines.Count; i++) + for (var i = 0; i < entityComponentsEngines.count; i++) try { using (profiler.Sample(entityComponentsEngines[i], _typeName)) - (entityComponentsEngines[i] as IReactOnSwap).MovedFrom(ref entity, egid); + { + (entityComponentsEngines[i] as IReactOnSwap).MovedTo( + ref entity, previousGroup.Value, egid); + } } - catch (Exception e) + catch (Exception) { - throw new ECSException( - "Code crashed inside Remove callback ".FastConcat(typeof(TValue).ToString()), e); + Svelto.Console.LogError("Code crashed inside MoveTo callback ".FastConcat(typeof(TValue).ToString())); + + throw; } } -#endif - } - - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public TValue[] GetValuesArray(out uint count) - { - var managedBuffer = _implementation.GetValuesArray(out count); - return managedBuffer; - } - - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public bool ContainsKey(uint egidEntityId) { return _implementation.ContainsKey(egidEntityId); } - - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public void Add(uint egidEntityId, in TValue entityComponent) { _implementation.Add(egidEntityId, entityComponent); } - - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public FasterDictionary.FasterDictionaryKeyValueEnumerator GetEnumerator() - { - return _implementation.GetEnumerator(); - } - - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public ref TValue GetValueByRef(uint key) { return ref _implementation.GetValueByRef(key); } - - public ref TValue this[uint idEntityId] - { - [MethodImpl(MethodImplOptions.AggressiveInlining)] - get => ref _implementation.GetValueByRef(idEntityId); - } - - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public uint GetIndex(uint valueEntityId) { return _implementation.GetIndex(valueEntityId); } - - public TValue[] unsafeValues - { - [MethodImpl(MethodImplOptions.AggressiveInlining)] - get => _implementation.unsafeValues; } - public object GenerateSentinel() + public void Dispose() { - return default; + if (_isUmanaged) + implUnmgd.Dispose(); + else + implMgd.Dispose(); + + GC.SuppressFinalize(this); } - - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public bool TryGetValue(uint entityId, out TValue item) { return _implementation.TryGetValue(entityId, out item); } - - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public ref TValue GetOrCreate(uint idEntityId) { return ref _implementation.GetOrCreate(idEntityId); } - - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public bool TryFindIndex(uint entityId, out uint index) { return _implementation.TryFindIndex(entityId, out index); } - - internal FasterDictionary implementation => _implementation; - - readonly FasterDictionary _implementation; } } \ No newline at end of file diff --git a/Svelto.ECS/DataStructures/TypeSafeDictionaryUtilities.cs b/Svelto.ECS/DataStructures/TypeSafeDictionaryUtilities.cs index 4d21f9f..57af0d1 100644 --- a/Svelto.ECS/DataStructures/TypeSafeDictionaryUtilities.cs +++ b/Svelto.ECS/DataStructures/TypeSafeDictionaryUtilities.cs @@ -9,13 +9,5 @@ namespace Svelto.ECS.Internal return mapper; } - - internal static NativeEGIDMapper ToNativeEGIDMapper(this TypeSafeDictionary dic, - ExclusiveGroupStruct groupStructId) where T : unmanaged, IEntityComponent - { - var mapper = new NativeEGIDMapper(groupStructId, dic.implementation.ToNative()); - - return mapper; - } } } \ No newline at end of file diff --git a/Svelto.ECS/DataStructures/UnsafeArray.cs b/Svelto.ECS/DataStructures/UnsafeArray.cs index 4ba484f..a995dc6 100644 --- a/Svelto.ECS/DataStructures/UnsafeArray.cs +++ b/Svelto.ECS/DataStructures/UnsafeArray.cs @@ -9,12 +9,13 @@ namespace Svelto.ECS.DataStructures internal unsafe byte* ptr => _ptr; //expressed in bytes - internal uint capacity => _capacity; + internal int capacity => (int) _capacity; //expressed in bytes - internal uint count => _writeIndex; + internal int count => (int) _writeIndex; + //expressed in bytes - internal uint space => capacity - count; + internal int space => capacity - count; /// /// @@ -26,37 +27,36 @@ namespace Svelto.ECS.DataStructures #endif [MethodImpl(MethodImplOptions.AggressiveInlining)] - public ref T Get(uint index) where T : unmanaged + public ref T Get(uint index) where T : struct { unsafe { - T* buffer = (T*) ptr; - return ref buffer[index]; + return ref Unsafe.AsRef(Unsafe.Add(ptr, (int) index)); } } [MethodImpl(MethodImplOptions.AggressiveInlining)] - public void Set(uint index, in T value) where T : unmanaged + public void Set(uint index, in T value) where T : struct { unsafe { - int sizeOf = MemoryUtilities.SizeOf(); + uint sizeOf = (uint) MemoryUtilities.SizeOf(); uint writeIndex = (uint) (index * sizeOf); #if DEBUG && !PROFILE_SVELTO if (_capacity < writeIndex + sizeOf) throw new Exception("no writing authorized"); -#endif - T* buffer = (T*) ptr; - buffer[index] = value; +#endif + Unsafe.AsRef(Unsafe.Add(_ptr, (int) index)) = value; if (_writeIndex < writeIndex + sizeOf) _writeIndex = (uint) (writeIndex + sizeOf); } } + [MethodImpl(MethodImplOptions.AggressiveInlining)] - public void Add(in T value) where T : unmanaged + public void Add(in T value) where T : struct { unsafe { @@ -73,7 +73,15 @@ namespace Svelto.ECS.DataStructures } [MethodImpl(MethodImplOptions.AggressiveInlining)] - internal void Realloc(uint newCapacity) where T : unmanaged + public void Pop() where T : struct + { + var structSize = MemoryUtilities.SizeOf(); + + _writeIndex -= (uint)structSize; + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + internal void Realloc(uint newCapacity) { unsafe { @@ -84,9 +92,9 @@ namespace Svelto.ECS.DataStructures #endif if (newCapacity >= 0) { - newPointer = (byte*) MemoryUtilities.Alloc(newCapacity, allocator); + newPointer = (byte*) MemoryUtilities.Alloc(newCapacity, allocator); if (count > 0) - Unsafe.CopyBlock(newPointer, ptr, count); + Unsafe.CopyBlock(newPointer, ptr, (uint) count); } if (ptr != null) @@ -117,10 +125,17 @@ namespace Svelto.ECS.DataStructures _writeIndex = 0; } -#if UNITY_ECS + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public void SetCountTo(uint count) + { + _writeIndex = count; + } + +#if UNITY_COLLECTIONS [global::Unity.Collections.LowLevel.Unsafe.NativeDisableUnsafePtrRestriction] #endif unsafe byte* _ptr; + uint _writeIndex; uint _capacity; } diff --git a/Svelto.ECS/DataStructures/UnsafeBlob.cs b/Svelto.ECS/DataStructures/UnsafeBlob.cs index f5e47c3..a658f02 100644 --- a/Svelto.ECS/DataStructures/UnsafeBlob.cs +++ b/Svelto.ECS/DataStructures/UnsafeBlob.cs @@ -1,10 +1,10 @@ using System; using System.Runtime.CompilerServices; using Svelto.Common; -using Unity.Collections.LowLevel.Unsafe; namespace Svelto.ECS.DataStructures { + //ToDO to complete in future version of svelto, maybe removed public struct UnsafeArrayIndex { internal uint index; @@ -12,116 +12,119 @@ namespace Svelto.ECS.DataStructures } /// - /// Note: this must work inside burst, so it must follow burst restrictions - /// Note: All the svelto native structures + /// Note: this must work inside burst, so it must follow burst restrictions + /// Note: All the svelto native structures /// struct UnsafeBlob : IDisposable { - internal unsafe byte* ptr => _ptr; + internal unsafe byte* ptr { get; set; } + //expressed in bytes internal uint capacity { get; private set; } + //expressed in bytes internal uint size => _writeIndex - _readIndex; + //expressed in bytes internal uint space => capacity - size; /// /// internal Allocator allocator; -#if DEBUG && !PROFILE_SVELTO - internal uint id; -#endif - + [MethodImpl(MethodImplOptions.AggressiveInlining)] - internal void Write(in T item) where T : unmanaged + internal void Write(in T item) where T : struct { unsafe { var structSize = (uint) MemoryUtilities.SizeOf(); - + //the idea is, considering the wrap, a read pointer must always be behind a writer pointer -#if DEBUG && !PROFILE_SVELTO - if (space - (int)structSize < 0) +#if DEBUG && !PROFILE_SVELTO + if (space - (int) structSize < 0) throw new Exception("no writing authorized"); #endif var writeHead = _writeIndex % capacity; if (writeHead + structSize <= capacity) { - Unsafe.Write(_ptr + writeHead, item); + Unsafe.Write(ptr + writeHead, item); } else - //copy with wrap, will start to copy and wrap for the reminder + //copy with wrap, will start to copy and wrap for the reminder { var byteCountToEnd = capacity - writeHead; - fixed (T* readFrom = &item) - { - //read and copy the first portion of Item until the end of the stream - Unsafe.CopyBlock(_ptr + writeHead, readFrom, byteCountToEnd); + var localCopyToAvoidGcIssues = item; + //read and copy the first portion of Item until the end of the stream + Unsafe.CopyBlock(ptr + writeHead, Unsafe.AsPointer(ref localCopyToAvoidGcIssues), byteCountToEnd); - var restCount = structSize - byteCountToEnd; + var restCount = structSize - byteCountToEnd; - //read and copy the remainder - var @from = (byte*) readFrom; - Unsafe.CopyBlock(_ptr, @from + byteCountToEnd, restCount); - } + //read and copy the remainder + Unsafe.CopyBlock(ptr, (byte*) Unsafe.AsPointer(ref localCopyToAvoidGcIssues) + byteCountToEnd + , restCount); } - uint paddedStructSize = Align4(structSize); - + //this is may seems a waste if you are going to use an unsafeBlob just for bytes, but it's necessary for mixed types. + //it's still possible to use WriteUnaligned though + var paddedStructSize = MemoryUtilities.Align4(structSize); + _writeIndex += paddedStructSize; } } - - [MethodImpl(MethodImplOptions.AggressiveInlining)] - internal void WriteUnaligned(in T item) where T : unmanaged - { - unsafe - { - uint structSize = (uint) MemoryUtilities.SizeOf(); - - //the idea is, considering the wrap, a read pointer must always be behind a writer pointer -#if DEBUG && !PROFILE_SVELTO - if (space - (int)structSize < 0) - throw new Exception("no writing authorized"); -#endif - var pointer = _writeIndex % capacity; - if (pointer + structSize <= capacity) - Unsafe.Write(_ptr + pointer, item); - else - { - var byteCount = capacity - pointer; - fixed (T* readFrom = &item) - { - Unsafe.CopyBlockUnaligned(_ptr + pointer, readFrom, byteCount); - - var restCount = structSize - byteCount; - var @from = (byte*) readFrom; - Unsafe.CopyBlockUnaligned(_ptr, @from + byteCount, restCount); - } - } +// [MethodImpl(MethodImplOptions.AggressiveInlining)] +// //ToDo: remove this and create an UnsafeBlobUnaligned, used on NativeRingBuffer where T cannot change +// internal void WriteUnaligned(in T item) where T : struct +// { +// unsafe +// { +// var structSize = (uint) MemoryUtilities.SizeOf(); +// +// //the idea is, considering the wrap, a read pointer must always be behind a writer pointer +// #if DEBUG && !PROFILE_SVELTO +// if (space - (int) structSize < 0) +// throw new Exception("no writing authorized"); +// #endif +// var pointer = _writeIndex % capacity; +// +// if (pointer + structSize <= capacity) +// { +// Unsafe.Write(ptr + pointer, item); +// } +// else +// { +// var byteCount = capacity - pointer; +// +// var localCopyToAvoidGCIssues = item; +// +// Unsafe.CopyBlockUnaligned(ptr + pointer, Unsafe.AsPointer(ref localCopyToAvoidGCIssues), byteCount); +// +// var restCount = structSize - byteCount; +// Unsafe.CopyBlockUnaligned(ptr, (byte*) Unsafe.AsPointer(ref localCopyToAvoidGCIssues) + byteCount +// , restCount); +// } +// +// _writeIndex += structSize; +// } +// } - _writeIndex += structSize; - } - } - [MethodImpl(MethodImplOptions.AggressiveInlining)] - internal T Read() where T : unmanaged + internal T Read() where T : struct { unsafe { var structSize = (uint) MemoryUtilities.SizeOf(); - -#if DEBUG && !PROFILE_SVELTO + +#if DEBUG && !PROFILE_SVELTO if (size < structSize) //are there enough bytes to read? throw new Exception("dequeuing empty queue or unexpected type dequeued"); if (_readIndex > _writeIndex) throw new Exception("unexpected read"); #endif - var head = _readIndex % capacity; - uint paddedStructSize = Align4(structSize); + var head = _readIndex % capacity; + var paddedStructSize = MemoryUtilities.Align4(structSize); _readIndex += paddedStructSize; if (_readIndex == _writeIndex) @@ -134,78 +137,78 @@ namespace Svelto.ECS.DataStructures } if (head + paddedStructSize <= capacity) - { - return Unsafe.Read(_ptr + head); - } + return Unsafe.Read(ptr + head); T item = default; - T* destination = &item; var byteCountToEnd = capacity - head; - Unsafe.CopyBlock(destination, _ptr + head, byteCountToEnd); + Unsafe.CopyBlock(Unsafe.AsPointer(ref item), ptr + head, byteCountToEnd); var restCount = structSize - byteCountToEnd; - Unsafe.CopyBlock((byte*) destination + byteCountToEnd, ptr, restCount); + Unsafe.CopyBlock((byte*) Unsafe.AsPointer(ref item) + byteCountToEnd, ptr, restCount); return item; } } - + [MethodImpl(MethodImplOptions.AggressiveInlining)] - internal ref T Reserve(out UnsafeArrayIndex index) where T : unmanaged + internal ref T Reserve(out UnsafeArrayIndex index) where T : struct { unsafe { var sizeOf = (uint) MemoryUtilities.SizeOf(); - - T* buffer = (T *)(_ptr + _writeIndex); + + ref var buffer = ref Unsafe.AsRef(ptr + _writeIndex); + #if DEBUG && !PROFILE_SVELTO - if (_writeIndex > capacity) throw new Exception($"can't reserve if the writeIndex wrapped around the capacity, writeIndex {_writeIndex} capacity {capacity}"); - if (_writeIndex + sizeOf > capacity) throw new Exception("out of bound reserving"); -#endif + if (_writeIndex > capacity) + throw new Exception( + $"can't reserve if the writeIndex wrapped around the capacity, writeIndex {_writeIndex} capacity {capacity}"); + if (_writeIndex + sizeOf > capacity) + throw new Exception("out of bound reserving"); +#endif index = new UnsafeArrayIndex { capacity = capacity , index = _writeIndex }; - var align4 = Align4(sizeOf); + var align4 = MemoryUtilities.Align4(sizeOf); _writeIndex += align4; - - return ref buffer[0]; + + return ref buffer; } } - + [MethodImpl(MethodImplOptions.AggressiveInlining)] - internal ref T AccessReserved(UnsafeArrayIndex index) where T : unmanaged + internal ref T AccessReserved(UnsafeArrayIndex index) where T : struct { unsafe { #if DEBUG && !PROFILE_SVELTO var size = MemoryUtilities.SizeOf(); - if (index.index + size > capacity) throw new Exception($"out of bound access, index {index.index} size {size} capacity {capacity}"); -#endif - T* buffer = (T*) (_ptr + index.index); - - return ref buffer[0]; + if (index.index + size > capacity) + throw new Exception($"out of bound access, index {index.index} size {size} capacity {capacity}"); +#endif + return ref Unsafe.AsRef(ptr + index.index); } } [MethodImpl(MethodImplOptions.AggressiveInlining)] - internal void Realloc(uint newCapacity) where T : unmanaged + internal void Realloc(uint newCapacity) { unsafe { //be sure it's multiple of 4. Assuming that what we write is aligned to 4, then we will always have aligned wrapped heads - newCapacity = Align4(newCapacity); - + newCapacity = MemoryUtilities.Align4(newCapacity); + byte* newPointer = null; -#if DEBUG && !PROFILE_SVELTO +#if DEBUG && !PROFILE_SVELTO if (newCapacity <= capacity) throw new Exception("new capacity must be bigger than current"); -#endif +#endif if (newCapacity > 0) { - newPointer = (byte*) MemoryUtilities.Alloc(newCapacity, allocator); + newPointer = (byte*) MemoryUtilities.Alloc(newCapacity, allocator); if (size > 0) { var readerHead = _readIndex % capacity; @@ -214,8 +217,8 @@ namespace Svelto.ECS.DataStructures if (readerHead < writerHead) { //copy to the new pointer, from th reader position - uint currentSize = _writeIndex - _readIndex; - Unsafe.CopyBlock(newPointer, _ptr + readerHead, currentSize); + var currentSize = _writeIndex - _readIndex; + Unsafe.CopyBlock(newPointer, ptr + readerHead, currentSize); } //the assumption is that if size > 0 (so readerPointer and writerPointer are not the same) //writerHead wrapped and reached readerHead. so I have to copy from readerHead to the end @@ -224,19 +227,19 @@ namespace Svelto.ECS.DataStructures { var byteCountToEnd = capacity - readerHead; - Unsafe.CopyBlock(newPointer, _ptr + readerHead, byteCountToEnd); - Unsafe.CopyBlock(newPointer + byteCountToEnd, _ptr, writerHead); + Unsafe.CopyBlock(newPointer, ptr + readerHead, byteCountToEnd); + Unsafe.CopyBlock(newPointer + byteCountToEnd, ptr, writerHead); } } } - if (_ptr != null) - MemoryUtilities.Free((IntPtr) _ptr, allocator); + if (ptr != null) + MemoryUtilities.Free((IntPtr) ptr, allocator); _writeIndex = size; _readIndex = 0; - - _ptr = newPointer; + + ptr = newPointer; capacity = newCapacity; } } @@ -246,12 +249,12 @@ namespace Svelto.ECS.DataStructures { unsafe { - if (_ptr != null) - MemoryUtilities.Free((IntPtr) _ptr, allocator); + if (ptr != null) + MemoryUtilities.Free((IntPtr) ptr, allocator); - _ptr = null; + ptr = null; _writeIndex = 0; - capacity = 0; + capacity = 0; } } @@ -259,18 +262,9 @@ namespace Svelto.ECS.DataStructures public void Clear() { _writeIndex = 0; - _readIndex = 0; + _readIndex = 0; } - uint Align4(uint input) - { - return (uint)(Math.Ceiling(input / 4.0) * 4); - } - -#if UNITY_ECS - [NativeDisableUnsafePtrRestriction] -#endif - unsafe byte* _ptr; uint _writeIndex, _readIndex; } -} +} \ No newline at end of file diff --git a/Svelto.ECS/Debugger/ExclusiveGroupDebugger.cs b/Svelto.ECS/Debugger/ExclusiveGroupDebugger.cs new file mode 100644 index 0000000..b38e2dc --- /dev/null +++ b/Svelto.ECS/Debugger/ExclusiveGroupDebugger.cs @@ -0,0 +1,69 @@ +using Svelto.ECS; + +#if DEBUG +using System; +using System.Collections.Generic; +using System.Reflection; + +public static class ExclusiveGroupDebugger +{ + static ExclusiveGroupDebugger() + { + Assembly[] assemblies = AppDomain.CurrentDomain.GetAssemblies(); + foreach (Assembly assembly in assemblies) + { + Type[] types = assembly.GetTypes(); + + foreach (Type type in types) + { + if (type != null && type.IsClass && type.IsSealed && type.IsAbstract) //this means only static classes + { + var fields = type.GetFields(); + foreach (var field in fields) + { + if (field.IsStatic && typeof(ExclusiveGroup).IsAssignableFrom(field.FieldType)) + { + string name = $"{type.FullName}.{field.Name}"; + var group = (ExclusiveGroup) field.GetValue(null); + GroupMap.idToName[(ExclusiveGroupStruct) group] = name; + } + + if (field.IsStatic && typeof(ExclusiveGroupStruct).IsAssignableFrom(field.FieldType)) + { + string name = $"{type.FullName}.{field.Name}"; + var group = (ExclusiveGroupStruct) field.GetValue(null); + GroupMap.idToName[@group] = name; + } + } + } + } + } + } + + public static string ToName(this in ExclusiveGroupStruct group) + { + if (GroupMap.idToName.TryGetValue(group, out var name) == false) + name = $""; + + return name; + } +} + +public static class GroupMap +{ + static GroupMap() + { + GroupMap.idToName = new Dictionary(); + } + + internal static readonly Dictionary idToName; +} +#else +public static class ExclusiveGroupDebugger +{ + public static string ToName(this in ExclusiveGroupStruct group) + { + return ((uint)group).ToString(); + } +} +#endif \ No newline at end of file diff --git a/Svelto.ECS/DynamicEntityDescriptor.cs b/Svelto.ECS/DynamicEntityDescriptor.cs index df1da90..05aa65d 100644 --- a/Svelto.ECS/DynamicEntityDescriptor.cs +++ b/Svelto.ECS/DynamicEntityDescriptor.cs @@ -4,7 +4,7 @@ using Svelto.DataStructures; namespace Svelto.ECS { /// - /// DynamicEntityDescriptor can be used to add entity views to an existing EntityDescriptor that act as flags, + /// DynamicEntityDescriptor can be used to add entity components to an existing EntityDescriptor that act as flags, /// at building time. /// This method allocates, so it shouldn't be abused /// @@ -22,9 +22,9 @@ namespace Svelto.ECS //assign it after otherwise the previous copy will overwrite the value in case the item //is already present - ComponentsToBuild[length] = new ComponentBuilder + ComponentsToBuild[length] = new ComponentBuilder ( - new EntityInfoComponentView + new EntityInfoViewComponent { componentsToBuild = ComponentsToBuild } @@ -80,9 +80,9 @@ namespace Svelto.ECS //assign it after otherwise the previous copy will overwrite the value in case the item //is already present - localEntitiesToBuild[index] = new ComponentBuilder + localEntitiesToBuild[index] = new ComponentBuilder ( - new EntityInfoComponentView + new EntityInfoViewComponent { componentsToBuild = localEntitiesToBuild } @@ -100,7 +100,7 @@ namespace Svelto.ECS for (var i = 0; i < length; i++) { //the special entity already exists - if (defaultEntities[i].GetEntityComponentType() == EntityBuilderUtilities.ENTITY_STRUCT_INFO_VIEW) + if (defaultEntities[i].GetEntityComponentType() == ComponentBuilderUtilities.ENTITY_STRUCT_INFO_VIEW) { index = i; break; diff --git a/Svelto.ECS/ECSResources/ECSResources.cs b/Svelto.ECS/ECSResources/ECSResources.cs index f307b2b..a1effb4 100644 --- a/Svelto.ECS/ECSResources/ECSResources.cs +++ b/Svelto.ECS/ECSResources/ECSResources.cs @@ -9,6 +9,10 @@ namespace Svelto.ECS.Experimental public static implicit operator T(ECSResources ecsString) { return ResourcesECSDB.FromECS(ecsString.id); } } + /// + /// To do. Or we reuse the ID or we need to clear this + /// + /// static class ResourcesECSDB { static readonly FasterList _resources = new FasterList(); diff --git a/Svelto.ECS/ECSResources/ECSString.cs b/Svelto.ECS/ECSResources/ECSString.cs index b31e54e..2717aaa 100644 --- a/Svelto.ECS/ECSResources/ECSString.cs +++ b/Svelto.ECS/ECSResources/ECSString.cs @@ -1,18 +1,25 @@ using System; +using System.Runtime.InteropServices; namespace Svelto.ECS.Experimental { [Serialization.DoNotSerialize] + [StructLayout(LayoutKind.Explicit)] + /// + /// Note: I should extend this to reuse unused id + /// public struct ECSString:IEquatable { - uint _id; + [FieldOffset(0)] uint _id; + [FieldOffset(4)] uint _versioning; + [FieldOffset(0)] long _realID; - public ECSString(string newText) + public ECSString(string newText):this() { _id = ResourcesECSDB.ToECS(newText); } - - ECSString(uint id) + + ECSString(uint id):this() { _id = id; } @@ -21,11 +28,24 @@ namespace Svelto.ECS.Experimental { return ResourcesECSDB.FromECS(ecsString._id); } - + + /// + /// Note: Setting null String could be a good way to signal a disposing of the ID so that + /// it can be recycled. + /// Zero id must be a null string + /// + /// public void Set(string newText) { if (_id != 0) - ResourcesECSDB.resources(_id) = newText; + { + if (ResourcesECSDB.resources(_id).Equals(newText) == false) + { + ResourcesECSDB.resources(_id) = newText; + + _versioning++; + } + } else _id = ResourcesECSDB.ToECS(newText); } @@ -39,14 +59,34 @@ namespace Svelto.ECS.Experimental return new ECSString(id); } + public override string ToString() + { + return ResourcesECSDB.FromECS(_id); + } + public bool Equals(ECSString other) { - return other._id == _id; + return _realID == other._realID; } - public override string ToString() + public static bool operator==(ECSString options1, ECSString options2) { - return ResourcesECSDB.FromECS(_id); + return options1._realID == options2._realID; + } + + public static bool operator!=(ECSString options1, ECSString options2) + { + return options1._realID != options2._realID; + } + + public override bool Equals(object obj) + { + throw new NotSupportedException(); //this is on purpose + } + + public override int GetHashCode() + { + return _realID.GetHashCode(); } } } \ No newline at end of file diff --git a/Svelto.ECS/EGID.cs b/Svelto.ECS/EGID.cs index 176c19b..4945031 100644 --- a/Svelto.ECS/EGID.cs +++ b/Svelto.ECS/EGID.cs @@ -5,7 +5,6 @@ using System.Runtime.InteropServices; namespace Svelto.ECS { - //todo: add debug map [Serialization.DoNotSerialize] [Serializable] [StructLayout(LayoutKind.Explicit)] @@ -75,7 +74,7 @@ namespace Svelto.ECS public override string ToString() { - return "id ".FastConcat(entityID).FastConcat(" group ").FastConcat(groupID); + return "id ".FastConcat(entityID).FastConcat(" group ").FastConcat(groupID.ToName()); } } } diff --git a/Svelto.ECS/EGIDMapper.cs b/Svelto.ECS/EGIDMapper.cs index b3ce79c..e4a23d0 100644 --- a/Svelto.ECS/EGIDMapper.cs +++ b/Svelto.ECS/EGIDMapper.cs @@ -1,66 +1,67 @@ -using System; using System.Runtime.CompilerServices; +using Svelto.DataStructures; using Svelto.ECS.Internal; namespace Svelto.ECS { public readonly struct EGIDMapper where T : struct, IEntityComponent { - internal readonly ITypeSafeDictionary map; - public uint Length => map.Count; - public ExclusiveGroupStruct groupID { get; } + public uint length => _map.count; + public ExclusiveGroupStruct groupID { get; } - public EGIDMapper(ExclusiveGroupStruct groupStructId, ITypeSafeDictionary dic):this() + public EGIDMapper(ExclusiveGroupStruct groupStructId, ITypeSafeDictionary dic) : this() { groupID = groupStructId; - map = dic; + _map = dic; } [MethodImpl(MethodImplOptions.AggressiveInlining)] public ref T Entity(uint entityID) { #if DEBUG && !PROFILE_SVELTO - if (map.TryFindIndex(entityID, out var findIndex) == false) - throw new Exception("Entity not found in this group ".FastConcat(typeof(T).ToString())); + if (_map.TryFindIndex(entityID, out var findIndex) == false) + throw new System.Exception("Entity not found in this group ".FastConcat(typeof(T).ToString())); #else - map.TryFindIndex(entityID, out var findIndex); + _map.TryFindIndex(entityID, out var findIndex); #endif - return ref map.unsafeValues[(int) findIndex]; + return ref _map.GetDirectValueByRef(findIndex); } - + public bool TryGetEntity(uint entityID, out T value) { - if (map.TryFindIndex(entityID, out var index)) + if (_map != null && _map.TryFindIndex(entityID, out var index)) { - value = map.unsafeValues[index]; + value = _map.GetDirectValueByRef(index); return true; } value = default; return false; } - - public T[] GetArrayAndEntityIndex(uint entityID, out uint index) + + public IBuffer GetArrayAndEntityIndex(uint entityID, out uint index) { - if (map.TryFindIndex(entityID, out index)) + if (_map.TryFindIndex(entityID, out index)) { - return map.unsafeValues; + return _map.GetValues(out _); } throw new ECSException("Entity not found"); } - - public bool TryGetArrayAndEntityIndex(uint entityID, out uint index, out T[] array) + + public bool TryGetArrayAndEntityIndex(uint entityID, out uint index, out IBuffer array) { - if (map.TryFindIndex(entityID, out index)) + index = default; + if (_map != null && _map.TryFindIndex(entityID, out index)) { - array = map.unsafeValues; + array = _map.GetValues(out _); return true; } array = default; return false; } + + readonly ITypeSafeDictionary _map; } -} - +} \ No newline at end of file diff --git a/Svelto.ECS/EnginesRoot.DoubleBufferedEntitiesToAdd.cs b/Svelto.ECS/EnginesRoot.DoubleBufferedEntitiesToAdd.cs new file mode 100644 index 0000000..4e1eae0 --- /dev/null +++ b/Svelto.ECS/EnginesRoot.DoubleBufferedEntitiesToAdd.cs @@ -0,0 +1,148 @@ +using System; +using Svelto.DataStructures; +using Svelto.ECS.Internal; + +namespace Svelto.ECS +{ + public partial class EnginesRoot + { + internal class DoubleBufferedEntitiesToAdd + { + const int MaximumNumberOfItemsPerFrameBeforeToClear = 100; + + internal void Swap() + { + Swap(ref current, ref other); + Swap(ref currentEntitiesCreatedPerGroup, ref otherEntitiesCreatedPerGroup); + } + + void Swap(ref T item1, ref T item2) + { + var toSwap = item2; + item2 = item1; + item1 = toSwap; + } + + public void ClearOther() + { + //do not clear the groups created so far, they will be reused, unless they are too many! + var otherCount = other.count; + if (otherCount > MaximumNumberOfItemsPerFrameBeforeToClear) + { + FasterDictionary, ITypeSafeDictionary>[] otherValuesArray = other.unsafeValues; + for (int i = 0; i < otherCount; ++i) + { + var safeDictionariesCount = otherValuesArray[i].count; + ITypeSafeDictionary[] safeDictionaries = otherValuesArray[i].unsafeValues; + { + for (int j = 0; j < safeDictionariesCount; ++j) + { + //clear the dictionary of entities create do far (it won't allocate though) + safeDictionaries[j].Dispose(); + } + } + } + + otherEntitiesCreatedPerGroup.FastClear(); + other.FastClear(); + return; + } + + { + FasterDictionary, ITypeSafeDictionary>[] otherValuesArray = other.unsafeValues; + for (int i = 0; i < otherCount; ++i) + { + var safeDictionariesCount = otherValuesArray[i].count; + ITypeSafeDictionary[] safeDictionaries = otherValuesArray[i].unsafeValues; + //do not remove the dictionaries of entities per type created so far, they will be reused + if (safeDictionariesCount <= MaximumNumberOfItemsPerFrameBeforeToClear) + { + for (int j = 0; j < safeDictionariesCount; ++j) + { + //clear the dictionary of entities create do far (it won't allocate though) + safeDictionaries[j].FastClear(); + } + } + else + { + for (int j = 0; j < safeDictionariesCount; ++j) + { + //clear the dictionary of entities create do far (it won't allocate though) + safeDictionaries[j].Dispose(); + } + + otherValuesArray[i].FastClear(); + } + } + + otherEntitiesCreatedPerGroup.FastClear(); + } + } + + /// + /// To avoid extra allocation, I don't clear the dictionaries, so I need an extra data structure + /// to keep count of the number of entities submitted this frame + /// + internal FasterDictionary currentEntitiesCreatedPerGroup; + internal FasterDictionary otherEntitiesCreatedPerGroup; + + //Before I tried for the third time to use a SparseSet instead of FasterDictionary, remember that + //while group indices are sequential, they may not be used in a sequential order. Sparaset needs + //entities to be created sequentially (the index cannot be managed externally) + internal FasterDictionary, ITypeSafeDictionary>> current; + internal FasterDictionary, ITypeSafeDictionary>> other; + + readonly FasterDictionary, ITypeSafeDictionary>> + _entityComponentsToAddBufferA = + new FasterDictionary, ITypeSafeDictionary>>(); + + readonly FasterDictionary, ITypeSafeDictionary>> + _entityComponentsToAddBufferB = + new FasterDictionary, ITypeSafeDictionary>>(); + + readonly FasterDictionary _entitiesCreatedPerGroupA = new FasterDictionary(); + readonly FasterDictionary _entitiesCreatedPerGroupB = new FasterDictionary(); + + public DoubleBufferedEntitiesToAdd() + { + currentEntitiesCreatedPerGroup = _entitiesCreatedPerGroupA; + otherEntitiesCreatedPerGroup = _entitiesCreatedPerGroupB; + + current = _entityComponentsToAddBufferA; + other = _entityComponentsToAddBufferB; + } + + public void Dispose() + { + { + var otherValuesArray = other.unsafeValues; + for (int i = 0; i < other.count; ++i) + { + var safeDictionariesCount = otherValuesArray[i].count; + var safeDictionaries = otherValuesArray[i].unsafeValues; + //do not remove the dictionaries of entities per type created so far, they will be reused + for (int j = 0; j < safeDictionariesCount; ++j) + { + //clear the dictionary of entities create do far (it won't allocate though) + safeDictionaries[j].Dispose(); + } + } + } + { + var currentValuesArray = current.unsafeValues; + for (int i = 0; i < current.count; ++i) + { + var safeDictionariesCount = currentValuesArray[i].count; + var safeDictionaries = currentValuesArray[i].unsafeValues; + //do not remove the dictionaries of entities per type created so far, they will be reused + for (int j = 0; j < safeDictionariesCount; ++j) + { + //clear the dictionary of entities create do far (it won't allocate though) + safeDictionaries[j].Dispose(); + } + } + } + } + } + } +} \ No newline at end of file diff --git a/Svelto.ECS/EnginesRoot.DoubleBufferedEntityViews.cs b/Svelto.ECS/EnginesRoot.DoubleBufferedEntityViews.cs deleted file mode 100644 index b602797..0000000 --- a/Svelto.ECS/EnginesRoot.DoubleBufferedEntityViews.cs +++ /dev/null @@ -1,93 +0,0 @@ -using System; -using Svelto.DataStructures; -using Svelto.ECS.Internal; - -namespace Svelto.ECS -{ - public partial class EnginesRoot - { - internal class DoubleBufferedEntitiesToAdd - { - const int MaximumNumberOfItemsPerFrameBeforeToClear = 100; - - internal void Swap() - { - Swap(ref current, ref other); - Swap(ref currentEntitiesCreatedPerGroup, ref otherEntitiesCreatedPerGroup); - } - - void Swap(ref T item1, ref T item2) - { - var toSwap = item2; - item2 = item1; - item1 = toSwap; - } - - public void ClearOther() - { - //do not clear the groups created so far, they will be reused, unless they are too many! - var otherCount = other.count; - if (otherCount > MaximumNumberOfItemsPerFrameBeforeToClear) - { - otherEntitiesCreatedPerGroup.FastClear(); - other.FastClear(); - return; - } - var otherValuesArray = other.unsafeValues; - for (int i = 0; i < otherCount; ++i) - { - var safeDictionariesCount = otherValuesArray[i].count; - var safeDictionaries = otherValuesArray[i].unsafeValues; - //do not remove the dictionaries of entities per type created so far, they will be reused - if (safeDictionariesCount <= MaximumNumberOfItemsPerFrameBeforeToClear) - { - for (int j = 0; j < safeDictionariesCount; ++j) - { - //clear the dictionary of entities create do far (it won't allocate though) - safeDictionaries[j].FastClear(); - } - } - else - { - otherValuesArray[i].FastClear(); - } - } - - otherEntitiesCreatedPerGroup.FastClear(); - } - - /// - /// To avoid extra allocation, I don't clear the dictionaries, so I need an extra data structure - /// to keep count of the number of entities submitted this frame - /// - internal FasterDictionary currentEntitiesCreatedPerGroup; - internal FasterDictionary otherEntitiesCreatedPerGroup; - - //Before I tried for the third time to use a SparseSet instead of FasterDictionary, remember that - //while group indices are sequential, they may not be used in a sequential order. Sparaset needs - //entities to be created sequentially (the index cannot be managed externally) - internal FasterDictionary, ITypeSafeDictionary>> current; - internal FasterDictionary, ITypeSafeDictionary>> other; - - readonly FasterDictionary, ITypeSafeDictionary>> - _entityComponentsToAddBufferA = - new FasterDictionary, ITypeSafeDictionary>>(); - - readonly FasterDictionary, ITypeSafeDictionary>> - _entityComponentsToAddBufferB = - new FasterDictionary, ITypeSafeDictionary>>(); - - readonly FasterDictionary _entitiesCreatedPerGroupA = new FasterDictionary(); - readonly FasterDictionary _entitiesCreatedPerGroupB = new FasterDictionary(); - - public DoubleBufferedEntitiesToAdd() - { - currentEntitiesCreatedPerGroup = _entitiesCreatedPerGroupA; - otherEntitiesCreatedPerGroup = _entitiesCreatedPerGroupB; - - current = _entityComponentsToAddBufferA; - other = _entityComponentsToAddBufferB; - } - } - } -} \ No newline at end of file diff --git a/Svelto.ECS/EnginesRoot.Engines.cs b/Svelto.ECS/EnginesRoot.Engines.cs index 61cfafa..70c8266 100644 --- a/Svelto.ECS/EnginesRoot.Engines.cs +++ b/Svelto.ECS/EnginesRoot.Engines.cs @@ -7,7 +7,7 @@ using Svelto.ECS.Schedulers; namespace Svelto.ECS { - public partial class EnginesRoot + public sealed partial class EnginesRoot { public struct EntitiesSubmitter { @@ -24,7 +24,7 @@ namespace Svelto.ECS readonly Svelto.DataStructures.WeakReference _weakReference; } - + public IEntitiesSubmissionScheduler scheduler { get; } /// @@ -37,34 +37,37 @@ namespace Svelto.ECS /// public EnginesRoot(IEntitiesSubmissionScheduler entitiesComponentScheduler) { - _entitiesOperations = new ThreadSafeDictionary(); - serializationDescriptorMap = new SerializationDescriptorMap(); - _reactiveEnginesAddRemove = new FasterDictionary, FasterList>(); - _reactiveEnginesSwap = new FasterDictionary, FasterList>(); - _enginesSet = new FasterList(); - _enginesTypeSet = new HashSet(); - _disposableEngines = new FasterList(); + _entitiesOperations = new ThreadSafeDictionary(); + serializationDescriptorMap = new SerializationDescriptorMap(); + _reactiveEnginesAddRemove = new FasterDictionary, FasterList>(); + _reactiveEnginesSwap = new FasterDictionary, FasterList>(); + _enginesSet = new FasterList(); + _enginesTypeSet = new HashSet(); + _disposableEngines = new FasterList(); _transientEntitiesOperations = new FasterList(); - _groupEntityComponentsDB = new FasterDictionary, ITypeSafeDictionary>>(); - _groupsPerEntity = new FasterDictionary, FasterDictionary>(); + _groupEntityComponentsDB = + new FasterDictionary, ITypeSafeDictionary>>(); + _groupsPerEntity = new FasterDictionary, FasterDictionary>(); _groupedEntityToAdd = new DoubleBufferedEntitiesToAdd(); _entitiesStream = new EntitiesStream(); - _entitiesDB = new EntitiesDB(_groupEntityComponentsDB, _groupsPerEntity, _entitiesStream); + _entitiesDB = new EntitiesDB(_groupEntityComponentsDB, _groupsPerEntity, _entitiesStream); - scheduler = entitiesComponentScheduler; + scheduler = entitiesComponentScheduler; scheduler.onTick = new EntitiesSubmitter(this); -#if UNITY_ECS +#if UNITY_BURST AllocateNativeOperations(); #endif } - - public EnginesRoot(IEntitiesSubmissionScheduler entitiesComponentScheduler, bool isDeserializationOnly):this(entitiesComponentScheduler) + + public EnginesRoot + (IEntitiesSubmissionScheduler entitiesComponentScheduler, bool isDeserializationOnly) : + this(entitiesComponentScheduler) { _isDeserializationOnly = isDeserializationOnly; } - + /// /// Dispose an EngineRoot once not used anymore, so that all the /// engines are notified with the entities removed. @@ -74,21 +77,45 @@ namespace Svelto.ECS { using (var profiler = new PlatformProfiler("Final Dispose")) { - foreach (var groups in _groupEntityComponentsDB) + foreach (var engine in _disposableEngines) { - foreach (var entityList in groups.Value) + try { - entityList.Value.RemoveEntitiesFromEngines(_reactiveEnginesAddRemove, profiler, - new ExclusiveGroupStruct(groups.Key)); + engine.Dispose(); } + catch (Exception e) + { + Svelto.Console.LogException(e); + } + } + + foreach (FasterDictionary, ITypeSafeDictionary>>. + KeyValuePairFast groups in _groupEntityComponentsDB) + { + foreach (FasterDictionary, ITypeSafeDictionary>.KeyValuePairFast entityList in + groups.Value) + try + { + entityList.Value.RemoveEntitiesFromEngines(_reactiveEnginesAddRemove, profiler + , new ExclusiveGroupStruct(groups.Key)); + } + catch (Exception e) + { + Svelto.Console.LogException(e); + } + } + + foreach (FasterDictionary, ITypeSafeDictionary>>. + KeyValuePairFast groups in _groupEntityComponentsDB) + { + foreach (FasterDictionary, ITypeSafeDictionary>.KeyValuePairFast entityList in + groups.Value) + entityList.Value.Dispose(); } _groupEntityComponentsDB.Clear(); _groupsPerEntity.Clear(); - foreach (var engine in _disposableEngines) - engine.Dispose(); - _disposableEngines.Clear(); _enginesSet.Clear(); _enginesTypeSet.Clear(); @@ -97,13 +124,13 @@ namespace Svelto.ECS _entitiesOperations.Clear(); _transientEntitiesOperations.Clear(); - scheduler.Dispose(); #if DEBUG && !PROFILE_SVELTO _idCheckers.Clear(); #endif - _groupedEntityToAdd = null; - + _groupedEntityToAdd.Dispose(); _entitiesStream.Dispose(); + + scheduler.Dispose(); } GC.SuppressFinalize(this); @@ -116,17 +143,16 @@ namespace Svelto.ECS Dispose(); } - public void AddEngine(IEngine engine) { - var type = engine.GetType(); + var type = engine.GetType(); var refWrapper = new RefWrapper(type); DBC.ECS.Check.Require(engine != null, "Engine to add is invalid or null"); DBC.ECS.Check.Require( - _enginesTypeSet.Contains(refWrapper) == false || - type.ContainsCustomAttribute(typeof(AllowMultipleAttribute)) == true, - "The same engine has been added more than once, if intentional, use [AllowMultiple] class attribute " - .FastConcat(engine.ToString())); + _enginesTypeSet.Contains(refWrapper) == false + || type.ContainsCustomAttribute(typeof(AllowMultipleAttribute)) == true + , "The same engine has been added more than once, if intentional, use [AllowMultiple] class attribute " + .FastConcat(engine.ToString())); try { if (engine is IReactOnAddAndRemove viewEngine) @@ -149,7 +175,8 @@ namespace Svelto.ECS } catch (Exception e) { - throw new ECSException("Code crashed while adding engine ".FastConcat(engine.GetType().ToString(), " "), e); + throw new ECSException("Code crashed while adding engine ".FastConcat(engine.GetType().ToString(), " ") + , e); } } @@ -169,8 +196,8 @@ namespace Svelto.ECS } } - static void AddEngine(T engine, Type[] entityComponentTypes, - FasterDictionary, FasterList> engines) + static void AddEngine + (T engine, Type[] entityComponentTypes, FasterDictionary, FasterList> engines) where T : class, IEngine { for (var i = 0; i < entityComponentTypes.Length; i++) diff --git a/Svelto.ECS/EnginesRoot.Entities.cs b/Svelto.ECS/EnginesRoot.Entities.cs index 2b8eae7..f25e6a3 100644 --- a/Svelto.ECS/EnginesRoot.Entities.cs +++ b/Svelto.ECS/EnginesRoot.Entities.cs @@ -1,14 +1,14 @@ using System; using System.Collections.Generic; using System.Runtime.CompilerServices; +using DBC.ECS; using Svelto.Common; using Svelto.DataStructures; using Svelto.ECS.Internal; -using Svelto.ECS.Schedulers; namespace Svelto.ECS { - public partial class EnginesRoot : IDisposable + public partial class EnginesRoot : IDisposable, IUnitTestingInterface { ///-------------------------------------------- /// @@ -16,25 +16,19 @@ namespace Svelto.ECS { return new GenericEntityStreamConsumerFactory(this); } - - public IEntityFactory GenerateEntityFactory() - { - return new GenericEntityFactory(this); - } - - public IEntityFunctions GenerateEntityFunctions() - { - return new GenericEntityFunctions(this); - } + public IEntityFactory GenerateEntityFactory() { return new GenericEntityFactory(this); } + public IEntityFunctions GenerateEntityFunctions() { return new GenericEntityFunctions(this); } ///-------------------------------------------- [MethodImpl(MethodImplOptions.AggressiveInlining)] - EntityComponentInitializer BuildEntity(EGID entityID, IComponentBuilder[] componentsToBuild, - IEnumerable implementors = null) + EntityComponentInitializer BuildEntity + (EGID entityID, IComponentBuilder[] componentsToBuild, Type implementorType, IEnumerable implementors = null) { - CheckAddEntityID(entityID); + CheckAddEntityID(entityID, implementorType); + Check.Require(entityID.groupID != 0, "invalid group detected"); - var dic = EntityFactory.BuildGroupedEntities(entityID, _groupedEntityToAdd, componentsToBuild, implementors); + var dic = EntityFactory.BuildGroupedEntities(entityID, _groupedEntityToAdd, componentsToBuild + , implementors); return new EntityComponentInitializer(entityID, dic); } @@ -42,85 +36,95 @@ namespace Svelto.ECS ///-------------------------------------------- void Preallocate(ExclusiveGroupStruct groupID, uint size) where T : IEntityDescriptor, new() { - var entityComponentsToBuild = EntityDescriptorTemplate.descriptor.componentsToBuild; - var numberOfEntityComponents = entityComponentsToBuild.Length; + using (var profiler = new PlatformProfiler("Preallocate")) + { + var entityComponentsToBuild = EntityDescriptorTemplate.descriptor.componentsToBuild; + var numberOfEntityComponents = entityComponentsToBuild.Length; - FasterDictionary, ITypeSafeDictionary> group = GetOrCreateGroup(groupID); + FasterDictionary, ITypeSafeDictionary> group = GetOrCreateGroup(groupID, profiler); - for (var index = 0; index < numberOfEntityComponents; index++) - { - var entityComponentBuilder = entityComponentsToBuild[index]; - var entityComponentType = entityComponentBuilder.GetEntityComponentType(); + for (var index = 0; index < numberOfEntityComponents; index++) + { + var entityComponentBuilder = entityComponentsToBuild[index]; + var entityComponentType = entityComponentBuilder.GetEntityComponentType(); - var refWrapper = new RefWrapper(entityComponentType); - if (group.TryGetValue(refWrapper, out var dbList) == false) - group[refWrapper] = entityComponentBuilder.Preallocate(ref dbList, size); - else - dbList.SetCapacity(size); + var refWrapper = new RefWrapper(entityComponentType); + if (group.TryGetValue(refWrapper, out var dbList) == false) + group[refWrapper] = entityComponentBuilder.Preallocate(ref dbList, size); + else + dbList.SetCapacity(size); - if (_groupsPerEntity.TryGetValue(refWrapper, out var groupedGroup) == false) - groupedGroup = _groupsPerEntity[refWrapper] = new FasterDictionary(); + if (_groupsPerEntity.TryGetValue(refWrapper, out var groupedGroup) == false) + groupedGroup = _groupsPerEntity[refWrapper] = new FasterDictionary(); - groupedGroup[groupID] = dbList; + groupedGroup[groupID] = dbList; + } } } ///-------------------------------------------- /// - void MoveEntityFromAndToEngines(IComponentBuilder[] entityBuilders, EGID fromEntityGID, EGID? toEntityGID) + void MoveEntityFromAndToEngines(IComponentBuilder[] componentBuilders, EGID fromEntityGID, EGID? toEntityGID) { using (var sampler = new PlatformProfiler("Move Entity From Engines")) { var fromGroup = GetGroup(fromEntityGID.groupID); //Check if there is an EntityInfoView linked to this entity, if so it's a DynamicEntityDescriptor! - if (fromGroup.TryGetValue(new RefWrapper(EntityBuilderUtilities.ENTITY_STRUCT_INFO_VIEW), - out var entityInfoViewDic) && - (entityInfoViewDic as ITypeSafeDictionary).TryGetValue(fromEntityGID.entityID, - out var entityInfoView)) - MoveEntityComponents(fromEntityGID, toEntityGID, entityInfoView.componentsToBuild, fromGroup, sampler); + if (fromGroup.TryGetValue(new RefWrapper(ComponentBuilderUtilities.ENTITY_STRUCT_INFO_VIEW) + , out var entityInfoViewDic) + && (entityInfoViewDic as ITypeSafeDictionary).TryGetValue( + fromEntityGID.entityID, out var entityInfoView)) + MoveEntityComponents(fromEntityGID, toEntityGID, entityInfoView.componentsToBuild, fromGroup + , sampler); //otherwise it's a normal static entity descriptor else - MoveEntityComponents(fromEntityGID, toEntityGID, entityBuilders, fromGroup, sampler); + MoveEntityComponents(fromEntityGID, toEntityGID, componentBuilders, fromGroup, sampler); } } - void MoveEntityComponents(EGID fromEntityGID, EGID? toEntityGID, IComponentBuilder[] entitiesToMove, - FasterDictionary, ITypeSafeDictionary> fromGroup, - PlatformProfiler sampler) + void MoveEntityComponents(EGID fromEntityGID, EGID? toEntityGID, IComponentBuilder[] entitiesToMove + , FasterDictionary, ITypeSafeDictionary> fromGroup, in PlatformProfiler sampler) { - FasterDictionary, ITypeSafeDictionary> toGroup = null; - - if (toEntityGID != null) + using (sampler.Sample("MoveEntityComponents")) { - var toGroupID = toEntityGID.Value.groupID; + var length = entitiesToMove.Length; - toGroup = GetOrCreateGroup(toGroupID); + FasterDictionary, ITypeSafeDictionary> toGroup = null; - //Add all the entities to the dictionary - for (var i = 0; i < entitiesToMove.Length; i++) - CopyEntityToDictionary(fromEntityGID, toEntityGID.Value, fromGroup, toGroup, - entitiesToMove[i].GetEntityComponentType()); - } + if (toEntityGID != null) + { + var toGroupID = toEntityGID.Value.groupID; + + toGroup = GetOrCreateGroup(toGroupID, sampler); + + //Add all the entities to the dictionary + for (var i = 0; i < length; i++) + CopyEntityToDictionary(fromEntityGID, toEntityGID.Value, fromGroup, toGroup + , entitiesToMove[i].GetEntityComponentType(), sampler); + } - //call all the callbacks - for (var i = 0; i < entitiesToMove.Length; i++) - MoveEntityComponentFromAndToEngines(fromEntityGID, toEntityGID, fromGroup, toGroup, - entitiesToMove[i].GetEntityComponentType(), sampler); + //call all the callbacks + for (var i = 0; i < length; i++) + MoveEntityComponentFromAndToEngines(fromEntityGID, toEntityGID, fromGroup, toGroup + , entitiesToMove[i].GetEntityComponentType(), sampler); - //then remove all the entities from the dictionary - for (var i = 0; i < entitiesToMove.Length; i++) - RemoveEntityFromDictionary(fromEntityGID, fromGroup, entitiesToMove[i].GetEntityComponentType(), sampler); + //then remove all the entities from the dictionary + for (var i = 0; i < length; i++) + RemoveEntityFromDictionary(fromEntityGID, fromGroup, entitiesToMove[i].GetEntityComponentType(), sampler); + } } - void CopyEntityToDictionary(EGID entityGID, EGID toEntityGID, - FasterDictionary, ITypeSafeDictionary> fromGroup, - FasterDictionary, ITypeSafeDictionary> toGroup, - Type entityComponentType) + void CopyEntityToDictionary + (EGID entityGID, EGID toEntityGID, FasterDictionary, ITypeSafeDictionary> fromGroup + , FasterDictionary, ITypeSafeDictionary> toGroup, Type entityComponentType, in PlatformProfiler sampler) { - var wrapper = new RefWrapper(entityComponentType); + using (sampler.Sample("CopyEntityToDictionary")) + { + var wrapper = new RefWrapper(entityComponentType); - ITypeSafeDictionary fromTypeSafeDictionary = GetTypeSafeDictionary(entityGID.groupID, fromGroup, wrapper); + ITypeSafeDictionary fromTypeSafeDictionary = + GetTypeSafeDictionary(entityGID.groupID, fromGroup, wrapper); #if DEBUG && !PROFILE_SVELTO if (fromTypeSafeDictionary.Has(entityGID.entityID) == false) @@ -128,47 +132,50 @@ namespace Svelto.ECS throw new EntityNotFoundException(entityGID, entityComponentType); } #endif - ITypeSafeDictionary toEntitiesDictionary = - GetOrCreateTypeSafeDictionary(toEntityGID.groupID, toGroup, wrapper, fromTypeSafeDictionary); + ITypeSafeDictionary toEntitiesDictionary = + GetOrCreateTypeSafeDictionary(toEntityGID.groupID, toGroup, wrapper, fromTypeSafeDictionary); - fromTypeSafeDictionary.AddEntityToDictionary(entityGID, toEntityGID, toEntitiesDictionary); + fromTypeSafeDictionary.AddEntityToDictionary(entityGID, toEntityGID, toEntitiesDictionary); + } } - void MoveEntityComponentFromAndToEngines(EGID entityGID, EGID? toEntityGID, - FasterDictionary, ITypeSafeDictionary> fromGroup, - FasterDictionary, ITypeSafeDictionary> toGroup, - Type entityComponentType, in PlatformProfiler profiler) + void MoveEntityComponentFromAndToEngines + (EGID entityGID, EGID? toEntityGID, FasterDictionary, ITypeSafeDictionary> fromGroup + , FasterDictionary, ITypeSafeDictionary> toGroup, Type entityComponentType + , in PlatformProfiler profiler) { - //add all the entities - var refWrapper = new RefWrapper(entityComponentType); - var fromTypeSafeDictionary = GetTypeSafeDictionary(entityGID.groupID, fromGroup, refWrapper); + using (profiler.Sample("MoveEntityComponentFromAndToEngines")) + { + //add all the entities + var refWrapper = new RefWrapper(entityComponentType); + var fromTypeSafeDictionary = GetTypeSafeDictionary(entityGID.groupID, fromGroup, refWrapper); - ITypeSafeDictionary toEntitiesDictionary = null; - if (toGroup != null) - toEntitiesDictionary = toGroup[refWrapper]; //this is guaranteed to exist by AddEntityToDictionary + ITypeSafeDictionary toEntitiesDictionary = null; + if (toGroup != null) + toEntitiesDictionary = toGroup[refWrapper]; //this is guaranteed to exist by AddEntityToDictionary #if DEBUG && !PROFILE_SVELTO if (fromTypeSafeDictionary.Has(entityGID.entityID) == false) throw new EntityNotFoundException(entityGID, entityComponentType); #endif - fromTypeSafeDictionary.MoveEntityFromEngines(entityGID, toEntityGID, toEntitiesDictionary, - toEntityGID == null ? _reactiveEnginesAddRemove : _reactiveEnginesSwap, in profiler); + fromTypeSafeDictionary.MoveEntityFromEngines(entityGID, toEntityGID, toEntitiesDictionary + , toEntityGID == null + ? _reactiveEnginesAddRemove + : _reactiveEnginesSwap, in profiler); + } } - void RemoveEntityFromDictionary(EGID entityGID, - FasterDictionary, ITypeSafeDictionary> fromGroup, - Type entityComponentType, in PlatformProfiler profiler) + [MethodImpl(MethodImplOptions.AggressiveInlining)] + void RemoveEntityFromDictionary + (EGID entityGID, FasterDictionary, ITypeSafeDictionary> fromGroup, Type entityComponentType + , in PlatformProfiler sampler) { - var refWrapper = new RefWrapper(entityComponentType); - var fromTypeSafeDictionary = GetTypeSafeDictionary(entityGID.groupID, fromGroup, refWrapper); - - fromTypeSafeDictionary.RemoveEntityFromDictionary(entityGID); - - //if (fromTypeSafeDictionary.Count == 0) //clean up + using (sampler.Sample("RemoveEntityFromDictionary")) { - //todo: this must be unit tested properly - //_groupsPerEntity[refWrapper].Remove(entityGID.groupID); - //I don't remove the group if empty on purpose, in case it needs to be reused + var refWrapper = new RefWrapper(entityComponentType); + var fromTypeSafeDictionary = GetTypeSafeDictionary(entityGID.groupID, fromGroup, refWrapper); + + fromTypeSafeDictionary.RemoveEntityFromDictionary(entityGID); } } @@ -180,54 +187,63 @@ namespace Svelto.ECS /// void SwapEntitiesBetweenGroups(uint fromIdGroupId, uint toGroupId, in PlatformProfiler profiler) { - FasterDictionary, ITypeSafeDictionary> fromGroup = GetGroup(fromIdGroupId); - FasterDictionary, ITypeSafeDictionary> toGroup = GetOrCreateGroup(toGroupId); - - foreach (FasterDictionary, ITypeSafeDictionary>.KeyValuePairFast dictionaryOfEntities in - fromGroup) + using (profiler.Sample("SwapEntitiesBetweenGroups")) { - //call all the MoveTo callbacks - dictionaryOfEntities.Value.AddEntitiesToEngines(_reactiveEnginesAddRemove, dictionaryOfEntities.Value, - new ExclusiveGroupStruct(toGroupId), profiler); + FasterDictionary, ITypeSafeDictionary> fromGroup = GetGroup(fromIdGroupId); + FasterDictionary, ITypeSafeDictionary> toGroup = GetOrCreateGroup(toGroupId, profiler); - ITypeSafeDictionary toEntitiesDictionary = GetOrCreateTypeSafeDictionary(toGroupId, toGroup, - dictionaryOfEntities.Key, dictionaryOfEntities.Value); + foreach (FasterDictionary, ITypeSafeDictionary>.KeyValuePairFast dictionaryOfEntities + in fromGroup) + { + //call all the MoveTo callbacks + dictionaryOfEntities.Value.AddEntitiesToEngines(_reactiveEnginesAddRemove + , dictionaryOfEntities.Value + , new ExclusiveGroupStruct(toGroupId), profiler); - FasterDictionary groupsOfEntityType = - _groupsPerEntity[dictionaryOfEntities.Key]; + ITypeSafeDictionary toEntitiesDictionary = + GetOrCreateTypeSafeDictionary(toGroupId, toGroup, dictionaryOfEntities.Key + , dictionaryOfEntities.Value); + + FasterDictionary groupsOfEntityType = + _groupsPerEntity[dictionaryOfEntities.Key]; - ITypeSafeDictionary typeSafeDictionary = groupsOfEntityType[fromIdGroupId]; - toEntitiesDictionary.AddEntitiesFromDictionary(typeSafeDictionary, toGroupId); + ITypeSafeDictionary typeSafeDictionary = groupsOfEntityType[fromIdGroupId]; + toEntitiesDictionary.AddEntitiesFromDictionary(typeSafeDictionary, toGroupId); - typeSafeDictionary.FastClear(); + typeSafeDictionary.FastClear(); + } } } FasterDictionary, ITypeSafeDictionary> GetGroup(uint fromIdGroupId) { - if (_groupEntityComponentsDB.TryGetValue(fromIdGroupId, - out FasterDictionary, ITypeSafeDictionary> fromGroup) == false) + if (_groupEntityComponentsDB.TryGetValue(fromIdGroupId + , out FasterDictionary, ITypeSafeDictionary> + fromGroup) == false) throw new ECSException("Group doesn't exist: ".FastConcat(fromIdGroupId)); + return fromGroup; } - FasterDictionary, ITypeSafeDictionary> GetOrCreateGroup(uint toGroupId) + FasterDictionary, ITypeSafeDictionary> GetOrCreateGroup(uint toGroupId, in PlatformProfiler profiler) { - if (_groupEntityComponentsDB.TryGetValue(toGroupId, - out FasterDictionary, ITypeSafeDictionary> toGroup) == false) - toGroup = _groupEntityComponentsDB[toGroupId] = - new FasterDictionary, ITypeSafeDictionary>(); - - return toGroup; + using (profiler.Sample("GetOrCreateGroup")) + { + if (_groupEntityComponentsDB.TryGetValue( + toGroupId, out FasterDictionary, ITypeSafeDictionary> toGroup) == false) + toGroup = _groupEntityComponentsDB[toGroupId] = + new FasterDictionary, ITypeSafeDictionary>(); + + return toGroup; + } } - ITypeSafeDictionary GetOrCreateTypeSafeDictionary(uint groupId, - FasterDictionary, ITypeSafeDictionary> toGroup, - RefWrapper type, ITypeSafeDictionary fromTypeSafeDictionary) + ITypeSafeDictionary GetOrCreateTypeSafeDictionary + (uint groupId, FasterDictionary, ITypeSafeDictionary> toGroup, RefWrapper type + , ITypeSafeDictionary fromTypeSafeDictionary) { //be sure that the TypeSafeDictionary for the entity Type exists - if (toGroup.TryGetValue(type, out ITypeSafeDictionary toEntitiesDictionary) == - false) + if (toGroup.TryGetValue(type, out ITypeSafeDictionary toEntitiesDictionary) == false) { toEntitiesDictionary = fromTypeSafeDictionary.Create(); toGroup.Add(type, toEntitiesDictionary); @@ -235,15 +251,14 @@ namespace Svelto.ECS //update GroupsPerEntity if (_groupsPerEntity.TryGetValue(type, out var groupedGroup) == false) - groupedGroup = _groupsPerEntity[type] = - new FasterDictionary(); + groupedGroup = _groupsPerEntity[type] = new FasterDictionary(); groupedGroup[groupId] = toEntitiesDictionary; return toEntitiesDictionary; } - static ITypeSafeDictionary GetTypeSafeDictionary(uint groupID, - FasterDictionary, ITypeSafeDictionary> @group, RefWrapper refWrapper) + static ITypeSafeDictionary GetTypeSafeDictionary + (uint groupID, FasterDictionary, ITypeSafeDictionary> @group, RefWrapper refWrapper) { if (@group.TryGetValue(refWrapper, out ITypeSafeDictionary fromTypeSafeDictionary) == false) { @@ -258,10 +273,10 @@ namespace Svelto.ECS FasterDictionary, ITypeSafeDictionary> dictionariesOfEntities = _groupEntityComponentsDB[groupID]; - foreach (var dictionaryOfEntities in dictionariesOfEntities) + foreach (FasterDictionary, ITypeSafeDictionary>.KeyValuePairFast dictionaryOfEntities in dictionariesOfEntities) { - dictionaryOfEntities.Value.RemoveEntitiesFromEngines(_reactiveEnginesAddRemove, profiler, - new ExclusiveGroupStruct(groupID)); + dictionaryOfEntities.Value.RemoveEntitiesFromEngines(_reactiveEnginesAddRemove, profiler + , new ExclusiveGroupStruct(groupID)); dictionaryOfEntities.Value.FastClear(); FasterDictionary groupsOfEntityType = @@ -275,7 +290,7 @@ namespace Svelto.ECS return _entitiesStream.GenerateConsumer(name, capacity); } - internal Consumer GenerateConsumer(ExclusiveGroup group, string name, uint capacity) + internal Consumer GenerateConsumer(ExclusiveGroupStruct group, string name, uint capacity) where T : unmanaged, IEntityComponent { return _entitiesStream.GenerateConsumer(group, name, capacity); @@ -288,7 +303,8 @@ namespace Svelto.ECS //ID. This ID doesn't need to be the EGID, it can be just the entityID //for each group id, save a dictionary indexed by entity type of entities indexed by id // group EntityComponentType entityID, EntityComponent - readonly FasterDictionary, ITypeSafeDictionary>> _groupEntityComponentsDB; + readonly FasterDictionary, ITypeSafeDictionary>> + _groupEntityComponentsDB; //for each entity view type, return the groups (dictionary of entities indexed by entity id) where they are //found indexed by group id. TypeSafeDictionary are never created, they instead point to the ones hold @@ -298,5 +314,12 @@ namespace Svelto.ECS readonly EntitiesDB _entitiesDB; readonly EntitiesStream _entitiesStream; + + EntitiesDB IUnitTestingInterface.entitiesForTesting => _entitiesDB; + } + + public interface IUnitTestingInterface + { + EntitiesDB entitiesForTesting { get; } } } \ No newline at end of file diff --git a/Svelto.ECS/EnginesRoot.GenericEntityFactory.cs b/Svelto.ECS/EnginesRoot.GenericEntityFactory.cs index 01a1baf..684d889 100644 --- a/Svelto.ECS/EnginesRoot.GenericEntityFactory.cs +++ b/Svelto.ECS/EnginesRoot.GenericEntityFactory.cs @@ -1,6 +1,6 @@ -using System.Collections.Generic; -using Svelto.DataStructures; -using Svelto.ECS.Internal; +using System; +using System.Collections.Generic; +using Svelto.Common; namespace Svelto.ECS { @@ -10,7 +10,7 @@ namespace Svelto.ECS { public GenericEntityFactory(EnginesRoot weakReference) { - _enginesRoot = new WeakReference(weakReference); + _enginesRoot = new Svelto.DataStructures.WeakReference(weakReference); } public EntityComponentInitializer BuildEntity @@ -19,26 +19,25 @@ namespace Svelto.ECS { return _enginesRoot.Target.BuildEntity(new EGID(entityID, groupStructId) , EntityDescriptorTemplate.descriptor.componentsToBuild - , implementors); + , TypeCache.type, implementors); } public EntityComponentInitializer BuildEntity(EGID egid, IEnumerable implementors = null) where T : IEntityDescriptor, new() { return _enginesRoot.Target.BuildEntity( - egid, EntityDescriptorTemplate.descriptor.componentsToBuild - , implementors); + egid, EntityDescriptorTemplate.descriptor.componentsToBuild, TypeCache.type, implementors); } public EntityComponentInitializer BuildEntity (EGID egid, T entityDescriptor, IEnumerable implementors) where T : IEntityDescriptor { - return _enginesRoot.Target.BuildEntity(egid, entityDescriptor.componentsToBuild, implementors); + return _enginesRoot.Target.BuildEntity(egid, entityDescriptor.componentsToBuild, TypeCache.type, implementors); } -#if UNITY_ECS - public NativeEntityFactory ToNative(Unity.Collections.Allocator allocator) where T : IEntityDescriptor, new() +#if UNITY_BURST + public NativeEntityFactory ToNative(string memberName) where T : IEntityDescriptor, new() { - return _enginesRoot.Target.ProvideNativeEntityFactoryQueue(); + return _enginesRoot.Target.ProvideNativeEntityFactoryQueue(memberName); } #endif public EntityComponentInitializer BuildEntity @@ -46,7 +45,7 @@ namespace Svelto.ECS where T : IEntityDescriptor { return _enginesRoot.Target.BuildEntity(new EGID(entityID, groupStructId) - , descriptorEntity.componentsToBuild, implementors); + , descriptorEntity.componentsToBuild, TypeCache.type, implementors); } public void PreallocateEntitySpace(ExclusiveGroupStruct groupStructId, uint size) @@ -54,10 +53,15 @@ namespace Svelto.ECS { _enginesRoot.Target.Preallocate(groupStructId, size); } + + public EntityComponentInitializer BuildEntity(EGID egid, IComponentBuilder[] componentsToBuild, Type type, IEnumerable implementors = null) + { + return _enginesRoot.Target.BuildEntity(egid, componentsToBuild, type, implementors); + } //enginesRoot is a weakreference because GenericEntityStreamConsumerFactory can be injected inside //engines of other enginesRoot - readonly WeakReference _enginesRoot; + readonly Svelto.DataStructures.WeakReference _enginesRoot; } } } \ No newline at end of file diff --git a/Svelto.ECS/EnginesRoot.GenericEntityFunctions.cs b/Svelto.ECS/EnginesRoot.GenericEntityFunctions.cs index 3d6f9eb..580f8fa 100644 --- a/Svelto.ECS/EnginesRoot.GenericEntityFunctions.cs +++ b/Svelto.ECS/EnginesRoot.GenericEntityFunctions.cs @@ -1,6 +1,6 @@ using System; -using System.Diagnostics; -using System.Runtime.CompilerServices; + using System.Runtime.CompilerServices; +using Svelto.Common; namespace Svelto.ECS { @@ -27,7 +27,8 @@ namespace Svelto.ECS [MethodImpl(MethodImplOptions.AggressiveInlining)] public void RemoveEntity(EGID entityEGID) where T : IEntityDescriptor, new() { - _enginesRoot.Target.CheckRemoveEntityID(entityEGID); + DBC.ECS.Check.Require(entityEGID.groupID != 0, "invalid group detected"); + _enginesRoot.Target.CheckRemoveEntityID(entityEGID, TypeCache.type); _enginesRoot.Target.QueueEntitySubmitOperation( new EntitySubmitOperation(EntitySubmitOperationType.Remove, entityEGID, entityEGID, @@ -50,6 +51,7 @@ namespace Svelto.ECS public void RemoveGroupAndEntities(ExclusiveGroupStruct groupID) { _enginesRoot.Target.RemoveGroupID(groupID); + DBC.ECS.Check.Require(groupID != 0, "invalid group detected"); _enginesRoot.Target.QueueEntitySubmitOperation( new EntitySubmitOperation(EntitySubmitOperationType.RemoveGroup, new EGID(0, groupID), new EGID())); @@ -103,15 +105,15 @@ namespace Svelto.ECS SwapEntityGroup(fromID, toID); } -#if UNITY_ECS - public NativeEntityRemove ToNativeRemove() where T : IEntityDescriptor, new() +#if UNITY_BURST + public NativeEntityRemove ToNativeRemove(string memberName) where T : IEntityDescriptor, new() { - return _enginesRoot.Target.ProvideNativeEntityRemoveQueue(); + return _enginesRoot.Target.ProvideNativeEntityRemoveQueue(memberName); } - public NativeEntitySwap ToNativeSwap() where T : IEntityDescriptor, new() + public NativeEntitySwap ToNativeSwap(string memberName) where T : IEntityDescriptor, new() { - return _enginesRoot.Target.ProvideNativeEntitySwapQueue(); + return _enginesRoot.Target.ProvideNativeEntitySwapQueue(memberName); } #endif @@ -119,8 +121,11 @@ namespace Svelto.ECS public void SwapEntityGroup(EGID fromID, EGID toID) where T : IEntityDescriptor, new() { - _enginesRoot.Target.CheckRemoveEntityID(fromID); - _enginesRoot.Target.CheckAddEntityID(toID); + DBC.ECS.Check.Require(fromID.groupID != 0, "invalid group detected"); + DBC.ECS.Check.Require(toID.groupID != 0, "invalid group detected"); + + _enginesRoot.Target.CheckRemoveEntityID(fromID, TypeCache.type); + _enginesRoot.Target.CheckAddEntityID(toID, TypeCache.type); _enginesRoot.Target.QueueEntitySubmitOperation( new EntitySubmitOperation(EntitySubmitOperationType.Swap, @@ -135,7 +140,7 @@ namespace Svelto.ECS void QueueEntitySubmitOperation(EntitySubmitOperation entitySubmitOperation) { #if DEBUG && !PROFILE_SVELTO - entitySubmitOperation.trace = new StackFrame(1, true); + entitySubmitOperation.trace = new System.Diagnostics.StackFrame(1, true); #endif _entitiesOperations.Add((ulong) entitySubmitOperation.fromID, entitySubmitOperation); } @@ -143,7 +148,7 @@ namespace Svelto.ECS void QueueEntitySubmitOperation(EntitySubmitOperation entitySubmitOperation) where T : IEntityDescriptor { #if DEBUG && !PROFILE_SVELTO - entitySubmitOperation.trace = new StackFrame(1, true); + entitySubmitOperation.trace = new System.Diagnostics.StackFrame(1, true); if (_entitiesOperations.TryGetValue((ulong) entitySubmitOperation.fromID, out var entitySubmitedOperation)) { diff --git a/Svelto.ECS/EnginesRoot.Submission.cs b/Svelto.ECS/EnginesRoot.Submission.cs index b1b546a..dfc049c 100644 --- a/Svelto.ECS/EnginesRoot.Submission.cs +++ b/Svelto.ECS/EnginesRoot.Submission.cs @@ -29,10 +29,9 @@ namespace Svelto.ECS void SingleSubmission(in PlatformProfiler profiler) { -#if UNITY_ECS +#if UNITY_BURST NativeOperationSubmission(profiler); #endif - if (_entitiesOperations.Count > 0) { using (profiler.Sample("Remove and Swap operations")) @@ -66,16 +65,19 @@ namespace Svelto.ECS break; } } - catch (Exception e) + catch { var str = "Crash while executing Entity Operation " .FastConcat(entitiesOperations[i].type.ToString()); - - throw new ECSException(str.FastConcat(" ") + + + Svelto.Console.LogError(str.FastConcat(" ") #if DEBUG && !PROFILE_SVELTO - .FastConcat(entitiesOperations[i].trace.ToString()) + .FastConcat(entitiesOperations[i].trace.ToString()) #endif - , e); + ); + + throw; } } } @@ -112,7 +114,7 @@ namespace Svelto.ECS { var groupID = groupOfEntitiesToSubmit.Key; - FasterDictionary, ITypeSafeDictionary> groupDB = GetOrCreateGroup(groupID); + FasterDictionary, ITypeSafeDictionary> groupDB = GetOrCreateGroup(groupID, profiler); //add the entityComponents in the group foreach (var entityComponentsToSubmit in _groupedEntityToAdd.other[groupID]) @@ -124,13 +126,13 @@ namespace Svelto.ECS ITypeSafeDictionary dbDic = GetOrCreateTypeSafeDictionary(groupID, groupDB, wrapper, targetTypeSafeDictionary); - //Fill the DB with the entity views generate this frame. + //Fill the DB with the entity components generate this frame. dbDic.AddEntitiesFromDictionary(targetTypeSafeDictionary, groupID); } } } - //then submit everything in the engines, so that the DB is up to date with all the entity views and struct + //then submit everything in the engines, so that the DB is up to date with all the entity components //created by the entity built using (profiler.Sample("Add entities to engines")) { diff --git a/Svelto.ECS/EntitiesDB.FindGroups.cs b/Svelto.ECS/EntitiesDB.FindGroups.cs new file mode 100644 index 0000000..63d8391 --- /dev/null +++ b/Svelto.ECS/EntitiesDB.FindGroups.cs @@ -0,0 +1,126 @@ +using System; +using System.Threading; +using Svelto.DataStructures; +using Svelto.ECS.Internal; + +namespace Svelto.ECS +{ + public partial class EntitiesDB + { + internal FasterDictionary FindGroups_INTERNAL() where T1 : IEntityComponent + { + if (_groupsPerEntity.ContainsKey(TypeRefWrapper.wrapper) == false) + return _emptyDictionary; + + return _groupsPerEntity[TypeRefWrapper.wrapper]; + } + + public LocalFasterReadOnlyList FindGroups() where T1 : IEntityComponent + { + FasterList result = groups.Value; + result.FastClear(); + if (_groupsPerEntity.TryGetValue(TypeRefWrapper.wrapper + , out FasterDictionary result1) == false) + return result; + + var result1Count = result1.count; + var fasterDictionaryNodes1 = result1.unsafeKeys; + + for (int j = 0; j < result1Count; j++) + { + result.Add(new ExclusiveGroupStruct(fasterDictionaryNodes1[j].key)); + } + + return result; + } + + public LocalFasterReadOnlyList FindGroups() where T1 : IEntityComponent where T2 : IEntityComponent + { + FasterList result = groups.Value; + result.FastClear(); + if (_groupsPerEntity.TryGetValue(TypeRefWrapper.wrapper + , out FasterDictionary result1) == false) + return result; + if (_groupsPerEntity.TryGetValue(TypeRefWrapper.wrapper + , out FasterDictionary result2) == false) + return result; + + var result1Count = result1.count; + var result2Count = result2.count; + var fasterDictionaryNodes1 = result1.unsafeKeys; + var fasterDictionaryNodes2 = result2.unsafeKeys; + + for (int i = 0; i < result1Count; i++) + { + for (int j = 0; j < result2Count; j++) + { + //if the same group is found used with both T1 and T2 + if (fasterDictionaryNodes1[i].key == fasterDictionaryNodes2[j].key) + { + result.Add(new ExclusiveGroupStruct(fasterDictionaryNodes1[i].key)); + break; + } + } + } + + return result; + } + + /// + /// Remember that this operation os O(N*M*P) where N,M,P are the number of groups where each component + /// is found. + /// + /// + /// + /// + /// + public LocalFasterReadOnlyList FindGroups() + where T1 : IEntityComponent where T2 : IEntityComponent where T3 : IEntityComponent + { + FindGroups(); + + FasterList result = groups.Value; + + if (result.count == 0) + return result; + + if (_groupsPerEntity.TryGetValue(TypeRefWrapper.wrapper + , out FasterDictionary result3) == false) + return result; + + var result3Count = result3.count; + var fasterDictionaryNodes3 = result3.unsafeKeys; + + for (int j = 0; j < result3Count; j++) + for (int i = (int) 0; i < result.count; i++) + { + if (fasterDictionaryNodes3[j].key == result[i]) + break; + + result.UnorderedRemoveAt(i); + i--; + } + + return result; + } + + struct GroupsList + { + static GroupsList() + { + groups = new FasterList(); + } + + static readonly FasterList groups; + + public static implicit operator FasterList(in GroupsList list) + { + return list.reference; + } + + FasterList reference => groups; + } + + static readonly ThreadLocal groups = new ThreadLocal(); + } +} \ No newline at end of file diff --git a/Svelto.ECS/EntitiesDB.cs b/Svelto.ECS/EntitiesDB.cs index 3ae6d2d..781f4dd 100644 --- a/Svelto.ECS/EntitiesDB.cs +++ b/Svelto.ECS/EntitiesDB.cs @@ -6,52 +6,38 @@ using System; using System.Runtime.CompilerServices; using Svelto.Common; using Svelto.DataStructures; +using Svelto.ECS.Hybrid; using Svelto.ECS.Internal; namespace Svelto.ECS { - public class EntitiesDB + public partial class EntitiesDB { - internal EntitiesDB( - FasterDictionary, ITypeSafeDictionary>> groupEntityComponentsDB, - FasterDictionary, FasterDictionary> groupsPerEntity, - EntitiesStream entityStream) + internal EntitiesDB + (FasterDictionary, ITypeSafeDictionary>> groupEntityComponentsDB + , FasterDictionary, FasterDictionary> groupsPerEntity + , EntitiesStream entityStream) { _groupEntityComponentsDB = groupEntityComponentsDB; - _groupsPerEntity = groupsPerEntity; - _entityStream = entityStream; + _groupsPerEntity = groupsPerEntity; + _entityStream = entityStream; } [MethodImpl(MethodImplOptions.AggressiveInlining)] public ref T QueryUniqueEntity(ExclusiveGroupStruct group) where T : struct, IEntityComponent { - var entities = QueryEntities(group).ToFastAccess(out var count); + var entities = QueryEntities(group); #if DEBUG && !PROFILE_SVELTO - if (count == 0) + if (entities.count == 0) throw new ECSException("Unique entity not found '".FastConcat(typeof(T).ToString()).FastConcat("'")); - if (count != 1) + if (entities.count != 1) throw new ECSException("Unique entities must be unique! '".FastConcat(typeof(T).ToString()) - .FastConcat("'")); + .FastConcat("'")); #endif return ref entities[0]; } - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public ref T QueryEntity(EGID entityGID) where T : struct, IEntityComponent - { - T[] array; - if ((array = QueryEntitiesAndIndexInternal(entityGID, out var index)) != null) - return ref array[(int) index]; - - throw new EntityNotFoundException(entityGID, typeof(T)); - } - - public ref T QueryEntity(uint id, ExclusiveGroupStruct group) where T : struct, IEntityComponent - { - return ref QueryEntity(new EGID(id, group)); - } - /// /// The QueryEntities follows the rule that entities could always be iterated regardless if they /// are 0, 1 or N. In case of 0 it returns an empty array. This allows to use the same for iteration @@ -63,105 +49,119 @@ namespace Svelto.ECS public EntityCollection QueryEntities(ExclusiveGroupStruct groupStructId) where T : struct, IEntityComponent { - T[] ret; - uint count = 0; - //object sentinel = default; + IBuffer buffer; + uint count = 0; + if (SafeQueryEntityDictionary(groupStructId, out var typeSafeDictionary) == false) - ret = RetrieveEmptyEntityComponentArray(); + buffer = RetrieveEmptyEntityComponentArray(); else { var safeDictionary = (typeSafeDictionary as ITypeSafeDictionary); - ret = safeDictionary.GetValuesArray(out count); - // sentinel = safeDictionary.GenerateSentinel(); + buffer = safeDictionary.GetValues(out count); } - return new EntityCollection(ret, count); + return new EntityCollection(buffer, count); } - public EntityCollection QueryEntities( - ExclusiveGroupStruct groupStruct) + public EntityCollection QueryEntities(ExclusiveGroupStruct groupStruct) where T1 : struct, IEntityComponent where T2 : struct, IEntityComponent { var T1entities = QueryEntities(groupStruct); var T2entities = QueryEntities(groupStruct); - +#if DEBUG && !PROFILE_SVELTO if (T1entities.count != T2entities.count) - throw new ECSException("Entity views count do not match in group. Entity 1: ' count: " - .FastConcat(T1entities.count).FastConcat(typeof(T1).ToString()) - .FastConcat("'. Entity 2: ' count: ".FastConcat(T2entities.count) - .FastConcat(typeof(T2).ToString()) - .FastConcat("'"))); + throw new ECSException("Entity components count do not match in group. Entity 1: ' count: " + .FastConcat(T1entities.count).FastConcat(" ", typeof(T1).ToString()) + .FastConcat("'. Entity 2: ' count: ".FastConcat(T2entities.count) + .FastConcat(" ", typeof(T2).ToString()) + .FastConcat("' group: ", groupStruct.ToName()))); +#endif return new EntityCollection(T1entities, T2entities); } - public EntityCollection - QueryEntities(ExclusiveGroupStruct groupStruct) + public EntityCollection QueryEntities(ExclusiveGroupStruct groupStruct) where T1 : struct, IEntityComponent where T2 : struct, IEntityComponent where T3 : struct, IEntityComponent { var T1entities = QueryEntities(groupStruct); var T2entities = QueryEntities(groupStruct); var T3entities = QueryEntities(groupStruct); - +#if DEBUG && !PROFILE_SVELTO if (T1entities.count != T2entities.count || T2entities.count != T3entities.count) - throw new ECSException("Entity views count do not match in group. Entity 1: " - .FastConcat(typeof(T1).ToString()).FastConcat(" count: ") - .FastConcat(T1entities.count) - .FastConcat(" Entity 2: " - .FastConcat(typeof(T2).ToString()).FastConcat(" count: ") - .FastConcat(T2entities.count) - .FastConcat(" Entity 3: ".FastConcat(typeof(T3).ToString())) - .FastConcat(" count: ").FastConcat(T3entities.count))); - - return new EntityCollection(T1entities, - T2entities, T3entities); + throw new ECSException("Entity components count do not match in group. Entity 1: " + .FastConcat(typeof(T1).ToString()).FastConcat(" count: ") + .FastConcat(T1entities.count).FastConcat( + " Entity 2: " + .FastConcat(typeof(T2).ToString()).FastConcat(" count: ") + .FastConcat(T2entities.count) + .FastConcat(" Entity 3: ".FastConcat(typeof(T3).ToString())) + .FastConcat(" count: ").FastConcat(T3entities.count))); +#endif + + return new EntityCollection(T1entities, T2entities, T3entities); } + + public int IterateOverGroupsAndCount + (in LocalFasterReadOnlyList groups) where T : struct, IEntityComponent + { + int count = 0; + + for (int i = 0; i < groups.count; i++) + { + count += Count(groups[i]); + } - public EntityCollections QueryEntities(ExclusiveGroup[] groups) where T : struct, IEntityComponent + return 0; + } + + public TupleRef QueryEntities + (in LocalFasterReadOnlyList groups) where T : struct, IEntityComponent { - return new EntityCollections(this, groups); + return new TupleRef(new EntityCollections(this, groups), new GroupsEnumerable(this, groups)); } - public EntityCollections QueryEntities(ExclusiveGroup[] groups) + public TupleRef QueryEntities(in LocalFasterReadOnlyList groups) where T1 : struct, IEntityComponent where T2 : struct, IEntityComponent { - return new EntityCollections(this, groups); + return new TupleRef(new EntityCollections(this, groups) + , new GroupsEnumerable(this, groups)); } - - public EntityCollections QueryEntities(ExclusiveGroup[] groups) + + public TupleRef QueryEntities(in LocalFasterReadOnlyList groups) where T1 : struct, IEntityComponent where T2 : struct, IEntityComponent where T3 : struct, IEntityComponent { - return new EntityCollections(this, groups); + return new TupleRef(new EntityCollections(this, groups) + , new GroupsEnumerable(this, groups)); } - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public EGIDMapper QueryMappedEntities(ExclusiveGroupStruct groupStructId) - where T : struct, IEntityComponent + public TupleRef QueryEntities + (in LocalFasterReadOnlyList groups) + where T1 : struct, IEntityComponent + where T2 : struct, IEntityComponent + where T3 : struct, IEntityComponent + where T4 : struct, IEntityComponent { - if (SafeQueryEntityDictionary(groupStructId, out var typeSafeDictionary) == false) - throw new EntityGroupNotFoundException(typeof(T)); - - return (typeSafeDictionary as ITypeSafeDictionary).ToEGIDMapper(groupStructId); + return new TupleRef(new EntityCollections(this, groups) + , new GroupsEnumerable(this, groups)); } [MethodImpl(MethodImplOptions.AggressiveInlining)] - public NativeEGIDMapper QueryNativeMappedEntities(ExclusiveGroupStruct groupStructId) - where T : unmanaged, IEntityComponent + public EGIDMapper QueryMappedEntities(ExclusiveGroupStruct groupStructId) + where T : struct, IEntityComponent { if (SafeQueryEntityDictionary(groupStructId, out var typeSafeDictionary) == false) throw new EntityGroupNotFoundException(typeof(T)); - return (typeSafeDictionary as TypeSafeDictionary).ToNativeEGIDMapper(groupStructId); + return (typeSafeDictionary as ITypeSafeDictionary).ToEGIDMapper(groupStructId); } - + [MethodImpl(MethodImplOptions.AggressiveInlining)] - public bool TryQueryMappedEntities(ExclusiveGroupStruct groupStructId, - out EGIDMapper mapper) - where T : struct, IEntityComponent + public bool TryQueryMappedEntities + (ExclusiveGroupStruct groupStructId, out EGIDMapper mapper) where T : struct, IEntityComponent { mapper = default; - if (SafeQueryEntityDictionary(groupStructId, out var typeSafeDictionary) == false || - typeSafeDictionary.Count == 0) + if (SafeQueryEntityDictionary(groupStructId, out var typeSafeDictionary) == false + || typeSafeDictionary.count == 0) return false; mapper = (typeSafeDictionary as ITypeSafeDictionary).ToEGIDMapper(groupStructId); @@ -169,60 +169,11 @@ namespace Svelto.ECS return true; } - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public bool TryQueryNativeMappedEntities(ExclusiveGroupStruct groupStructId, - out NativeEGIDMapper mapper) - where T : unmanaged, IEntityComponent - { - mapper = default; - if (SafeQueryEntityDictionary(groupStructId, out var typeSafeDictionary) == false || - typeSafeDictionary.Count == 0) - return false; - - mapper = (typeSafeDictionary as TypeSafeDictionary).ToNativeEGIDMapper(groupStructId); - - return true; - } - - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public T[] QueryEntitiesAndIndex(EGID entityGID, out uint index) where T : struct, IEntityComponent - { - T[] array; - if ((array = QueryEntitiesAndIndexInternal(entityGID, out index)) != null) - return array; - - throw new EntityNotFoundException(entityGID, typeof(T)); - } - - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public bool TryQueryEntitiesAndIndex(EGID entityGid, out uint index, out T[] array) - where T : struct, IEntityComponent - { - if ((array = QueryEntitiesAndIndexInternal(entityGid, out index)) != null) - return true; - - return false; - } - - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public T[] QueryEntitiesAndIndex(uint id, ExclusiveGroupStruct @group, out uint index) - where T : struct, IEntityComponent - { - return QueryEntitiesAndIndex(new EGID(id, @group), out index); - } - - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public bool TryQueryEntitiesAndIndex - (uint id, ExclusiveGroupStruct group, out uint index, out T[] array) - where T : struct, IEntityComponent - { - return TryQueryEntitiesAndIndex(new EGID(id, @group), out index, out array); - } - [MethodImpl(MethodImplOptions.AggressiveInlining)] public bool Exists(EGID entityGID) where T : struct, IEntityComponent { - if (SafeQueryEntityDictionary(entityGID.groupID, out var casted) == false) return false; + if (SafeQueryEntityDictionary(entityGID.groupID, out var casted) == false) + return false; return casted != null && casted.ContainsKey(entityGID.entityID); } @@ -230,7 +181,8 @@ namespace Svelto.ECS [MethodImpl(MethodImplOptions.AggressiveInlining)] public bool Exists(uint id, ExclusiveGroupStruct group) where T : struct, IEntityComponent { - if (SafeQueryEntityDictionary(group, out var casted) == false) return false; + if (SafeQueryEntityDictionary(group, out var casted) == false) + return false; return casted != null && casted.ContainsKey(id); } @@ -238,8 +190,8 @@ namespace Svelto.ECS [MethodImpl(MethodImplOptions.AggressiveInlining)] public bool ExistsAndIsNotEmpty(ExclusiveGroupStruct gid) { - if (_groupEntityComponentsDB.TryGetValue(gid, - out FasterDictionary, ITypeSafeDictionary> group) == true) + if (_groupEntityComponentsDB.TryGetValue( + gid, out FasterDictionary, ITypeSafeDictionary> group) == true) { return group.count > 0; } @@ -250,73 +202,59 @@ namespace Svelto.ECS [MethodImpl(MethodImplOptions.AggressiveInlining)] public bool HasAny(ExclusiveGroupStruct groupStruct) where T : struct, IEntityComponent { - return QueryEntities(groupStruct).count > 0; + return Count(groupStruct) > 0; } [MethodImpl(MethodImplOptions.AggressiveInlining)] - public uint Count(ExclusiveGroupStruct groupStruct) where T : struct, IEntityComponent + public int Count(ExclusiveGroupStruct groupStruct) where T : struct, IEntityComponent { - return QueryEntities(groupStruct).count; + if (SafeQueryEntityDictionary(groupStruct, out var typeSafeDictionary) == false) + return 0; + + return (int) typeSafeDictionary.count; } [MethodImpl(MethodImplOptions.AggressiveInlining)] public void PublishEntityChange(EGID egid) where T : unmanaged, IEntityComponent { - _entityStream.PublishEntity(ref QueryEntity(egid), egid); + _entityStream.PublishEntity(ref this.QueryEntity(egid), egid); } + [Obsolete("This Method will be removed soon. please use QueryEntities instead")] public void ExecuteOnAllEntities(ExecuteOnAllEntitiesAction action) where T : struct, IEntityComponent { if (_groupsPerEntity.TryGetValue(TypeRefWrapper.wrapper, out var dictionary)) foreach (var pair in dictionary) { - var entities = (pair.Value as ITypeSafeDictionary).GetValuesArray(out var count); + IBuffer entities = (pair.Value as ITypeSafeDictionary).GetValues(out var count); if (count > 0) action(entities, new ExclusiveGroupStruct(pair.Key), count, this); } } + [Obsolete("This Method will be removed soon. please use QueryEntities instead")] public void ExecuteOnAllEntities(ref W value, ExecuteOnAllEntitiesAction action) where T : struct, IEntityComponent { if (_groupsPerEntity.TryGetValue(TypeRefWrapper.wrapper, out var dic)) foreach (var pair in dic) { - var entities = (pair.Value as ITypeSafeDictionary).GetValuesArray(out var innerCount); + IBuffer entities = (pair.Value as ITypeSafeDictionary).GetValues(out var innerCount); if (innerCount > 0) - action(entities, new ExclusiveGroupStruct(pair.Key), innerCount, this, - ref value); + action(entities, new ExclusiveGroupStruct(pair.Key), innerCount, this, ref value); } } - public QueryGroups CreateQueryGroup() where T : IEntityComponent - { - return new QueryGroups(FindGroups()); - } - public bool FoundInGroups() where T1 : IEntityComponent { return _groupsPerEntity.ContainsKey(TypeRefWrapper.wrapper); } [MethodImpl(MethodImplOptions.AggressiveInlining)] - T[] QueryEntitiesAndIndexInternal(EGID entityGID, out uint index) where T : struct, IEntityComponent - { - index = 0; - if (SafeQueryEntityDictionary(entityGID.groupID, out var safeDictionary) == false) - return null; - - if (safeDictionary.TryFindIndex(entityGID.entityID, out index) == false) - return null; - - return (safeDictionary as ITypeSafeDictionary).unsafeValues; - } - - [MethodImpl(MethodImplOptions.AggressiveInlining)] - bool SafeQueryEntityDictionary(uint group, out ITypeSafeDictionary typeSafeDictionary) - where T : struct, IEntityComponent + internal bool SafeQueryEntityDictionary(uint group, out ITypeSafeDictionary typeSafeDictionary) + where T : IEntityComponent { if (UnsafeQueryEntityDictionary(group, TypeCache.type, out var safeDictionary) == false) { @@ -345,32 +283,43 @@ namespace Svelto.ECS } [MethodImpl(MethodImplOptions.AggressiveInlining)] - static T[] RetrieveEmptyEntityComponentArray() + static IBuffer RetrieveEmptyEntityComponentArray() where T : struct, IEntityComponent { return EmptyList.emptyArray; } - static class EmptyList + static class EmptyList where T : struct, IEntityComponent { - internal static readonly T[] emptyArray = new T[0]; - } + internal static readonly IBuffer emptyArray; - internal FasterDictionary FindGroups() where T1 : IEntityComponent - { - if (_groupsPerEntity.ContainsKey(TypeRefWrapper.wrapper) == false) - return _emptyDictionary; - - return _groupsPerEntity[TypeRefWrapper.wrapper]; + static EmptyList() + { + if (ComponentBuilder.IS_ENTITY_VIEW_COMPONENT) + { + MB b = default; + + emptyArray = b; + } + else + { + NB b = default; + + emptyArray = b; + } + } } - readonly FasterDictionary _emptyDictionary = new FasterDictionary(); + readonly FasterDictionary _emptyDictionary = + new FasterDictionary(); + readonly EntitiesStream _entityStream; - //grouped set of entity views, this is the standard way to handle entity views entity views are grouped per + //grouped set of entity components, this is the standard way to handle entity components are grouped per //group, then indexable per type, then indexable per EGID. however the TypeSafeDictionary can return an array of - //values directly, that can be iterated over, so that is possible to iterate over all the entity views of + //values directly, that can be iterated over, so that is possible to iterate over all the entity components of //a specific type inside a specific group. - readonly FasterDictionary, ITypeSafeDictionary>> _groupEntityComponentsDB; + readonly FasterDictionary, ITypeSafeDictionary>> + _groupEntityComponentsDB; //needed to be able to track in which groups a specific entity type can be found. //may change in future as it could be expanded to support queries diff --git a/Svelto.ECS/EntityCollection.cs b/Svelto.ECS/EntityCollection.cs index 9f7e9b7..7211113 100644 --- a/Svelto.ECS/EntityCollection.cs +++ b/Svelto.ECS/EntityCollection.cs @@ -1,69 +1,68 @@ -using System; using System.Runtime.CompilerServices; -using System.Runtime.InteropServices; -using System.Threading; +using Svelto.Common; using Svelto.DataStructures; +using Svelto.ECS.Internal; namespace Svelto.ECS { - public struct EntityCollection where T : IEntityComponent + public readonly ref struct EntityCollection where T : struct, IEntityComponent { - public EntityCollection(T[] array, uint count) : this() - { - _buffer.Set(array, count); - _count = count; - } - - public EntityCollection(MB buffer, uint count) + static readonly bool IsUnmanaged = TypeSafeDictionary._isUmanaged; + + public EntityCollection(IBuffer buffer, uint count):this() { - _buffer = buffer; + if (IsUnmanaged) + _nativedBuffer = (NB) buffer; + else + _managedBuffer = (MB) buffer; + _count = count; + _buffer = buffer; } public uint count => _count; - readonly MB _buffer; - readonly uint _count; - - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public T[] ToFastAccess(out uint actualCount) - { - actualCount = _count; - return _buffer.ToManagedArray(); - } - - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public NB ToNativeBuffer() where NT : unmanaged, T - { - return new NB(_buffer.Pin(), _count, _buffer.capacity); - } - - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public MB ToBuffer() - { - return _buffer; - } + internal readonly MB _managedBuffer; + internal readonly NB _nativedBuffer; + readonly uint _count; + //todo very likely remove this public ref T this[uint i] { [MethodImpl(MethodImplOptions.AggressiveInlining)] - get => ref _buffer[i]; + get + { + if (IsUnmanaged) + return ref _nativedBuffer[i]; + else + return ref _managedBuffer[i]; + } } public ref T this[int i] { [MethodImpl(MethodImplOptions.AggressiveInlining)] - get => ref _buffer[i]; + get + { + if (IsUnmanaged) + return ref _nativedBuffer[i]; + else + return ref _managedBuffer[i]; + } } + //TODO SOON: ALL THIS STUFF BELOW MUST DISAPPEAR + readonly IBuffer _buffer; + + //todo to remove [MethodImpl(MethodImplOptions.AggressiveInlining)] public EntityIterator GetEnumerator() { return new EntityIterator(_buffer, _count); } - - public struct EntityIterator +//todo to remove + public ref struct EntityIterator { - public EntityIterator(MB array, uint count) : this() + public EntityIterator(IBuffer array, uint count) : this() { - _array = array.ToManagedArray(); + _array = array; _count = count; _index = -1; } @@ -77,14 +76,13 @@ namespace Svelto.ECS get => ref _array[_index]; } - readonly T[] _array; - readonly uint _count; - int _index; + readonly IBuffer _array; + readonly uint _count; + int _index; } } - public struct EntityCollection - where T1 : IEntityComponent where T2 : IEntityComponent + public readonly ref struct EntityCollection where T1 : struct, IEntityComponent where T2 : struct, IEntityComponent { public EntityCollection(in EntityCollection array1, in EntityCollection array2) { @@ -93,13 +91,14 @@ namespace Svelto.ECS } public uint count => _array1.count; - + + //todo to remove public EntityCollection Item2 { [MethodImpl(MethodImplOptions.AggressiveInlining)] get => _array2; } - +//todo to remove public EntityCollection Item1 { [MethodImpl(MethodImplOptions.AggressiveInlining)] @@ -108,55 +107,23 @@ namespace Svelto.ECS readonly EntityCollection _array1; readonly EntityCollection _array2; - - public (T1[], T2[]) ToFastAccess(out uint count) - { - count = this.count; - - return (_array1.ToFastAccess(out _), _array2.ToFastAccess(out _)); - } - - public BT, MB> ToBuffers() - { - var bufferTuple = new BT, MB> - (_array1.ToBuffer(), _array2.ToBuffer(), count); - return bufferTuple; - } - - public BT, NB> ToNativeBuffers() - where NT2 : unmanaged, T2 where NT1 : unmanaged, T1 - { - var bufferTuple = new BT, NB> - (_array1.ToNativeBuffer(), _array2.ToNativeBuffer(), count); - - return bufferTuple; - } - +//todo to remove [MethodImpl(MethodImplOptions.AggressiveInlining)] - public EntityIterator GetEnumerator() - { - return new EntityIterator(this); - } - - public struct EntityIterator + public EntityIterator GetEnumerator() { return new EntityIterator(this); } +//todo to remove + public ref struct EntityIterator { public EntityIterator(in EntityCollection array1) : this() { _array1 = array1; - _count = array1.count; - _index = -1; + _count = array1.count; + _index = -1; } [MethodImpl(MethodImplOptions.AggressiveInlining)] - public bool MoveNext() - { - return ++_index < _count; - } + public bool MoveNext() { return ++_index < _count; } - public void Reset() - { - _index = -1; - } + public void Reset() { _index = -1; } public ValueRef Current { @@ -165,35 +132,36 @@ namespace Svelto.ECS } readonly EntityCollection _array1; - readonly uint _count; - int _index; + readonly uint _count; + int _index; } + } - public struct EntityCollection - where T3 : IEntityComponent where T2 : IEntityComponent where T1 : IEntityComponent + public readonly ref struct EntityCollection where T3 : struct, IEntityComponent + where T2 : struct, IEntityComponent + where T1 : struct, IEntityComponent { - public EntityCollection( - in EntityCollection array1, in EntityCollection array2, - in EntityCollection array3) + public EntityCollection + (in EntityCollection array1, in EntityCollection array2, in EntityCollection array3) { _array1 = array1; _array2 = array2; _array3 = array3; } - +//todo to remove public EntityCollection Item1 { [MethodImpl(MethodImplOptions.AggressiveInlining)] get => _array1; } - +//todo to remove public EntityCollection Item2 { [MethodImpl(MethodImplOptions.AggressiveInlining)] get => _array2; } - +//todo to remove public EntityCollection Item3 { [MethodImpl(MethodImplOptions.AggressiveInlining)] @@ -202,241 +170,69 @@ namespace Svelto.ECS public uint count => Item1.count; - public (T1[], T2[], T3[]) ToFastAccess(out uint count) - { - count = this.count; - - return (_array1.ToFastAccess(out _), _array2.ToFastAccess(out _), _array3.ToFastAccess(out _)); - } - - public BT, MB, MB> ToBuffers() - { - var bufferTuple = new BT, MB, MB> - (_array1.ToBuffer(), _array2.ToBuffer(), _array3.ToBuffer(), count); - return bufferTuple; - } - - public BT, NB, NB> ToNativeBuffers() - where NT2 : unmanaged, T2 where NT1 : unmanaged, T1 where NT3 : unmanaged, T3 - { - var bufferTuple = new BT, NB, NB> - (_array1.ToNativeBuffer(), _array2.ToNativeBuffer(), _array3.ToNativeBuffer(), count); - - return bufferTuple; - } - readonly EntityCollection _array1; readonly EntityCollection _array2; readonly EntityCollection _array3; } - - public struct EntityCollections where T : struct, IEntityComponent + + public readonly ref struct EntityCollection + where T1 : struct, IEntityComponent + where T2 : struct, IEntityComponent + where T3 : struct, IEntityComponent + where T4 : struct, IEntityComponent { - public EntityCollections(EntitiesDB db, ExclusiveGroup[] groups) : this() + public EntityCollection + (in EntityCollection array1, in EntityCollection array2, in EntityCollection array3, in EntityCollection array4) { - _db = db; - _groups = groups; + _array1 = array1; + _array2 = array2; + _array3 = array3; + _array4 = array4; } - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public EntityGroupsIterator GetEnumerator() + //todo to remove + public EntityCollection Item1 { - return new EntityGroupsIterator(_db, _groups); + [MethodImpl(MethodImplOptions.AggressiveInlining)] + get => _array1; } - readonly EntitiesDB _db; - readonly ExclusiveGroup[] _groups; - - public struct EntityGroupsIterator + //todo to remove + public EntityCollection Item2 { - public EntityGroupsIterator(EntitiesDB db, ExclusiveGroup[] groups) : this() - { - _db = db; - _groups = groups; - _indexGroup = -1; - _index = -1; - } - - public bool MoveNext() - { - //attention, the while is necessary to skip empty groups - while (_index + 1 >= _count && ++_indexGroup < _groups.Length) - { - _index = -1; - _array = _db.QueryEntities(_groups[_indexGroup]); - _count = _array.count; - } - - return ++_index < _count; - } - - public void Reset() - { - _index = -1; - _indexGroup = -1; - _count = 0; - } - - public ref T Current => ref _array[(uint) _index]; - - readonly EntitiesDB _db; - readonly ExclusiveGroup[] _groups; - - EntityCollection _array; - uint _count; - int _index; - int _indexGroup; + [MethodImpl(MethodImplOptions.AggressiveInlining)] + get => _array2; } - } - public struct EntityCollections - where T1 : struct, IEntityComponent where T2 : struct, IEntityComponent - { - public EntityCollections(EntitiesDB db, ExclusiveGroup[] groups) : this() + //todo to remove + public EntityCollection Item3 { - _db = db; - _groups = groups; + [MethodImpl(MethodImplOptions.AggressiveInlining)] + get => _array3; } - - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public EntityGroupsIterator GetEnumerator() + + //todo to remove + public EntityCollection Item4 { - return new EntityGroupsIterator(_db, _groups); + [MethodImpl(MethodImplOptions.AggressiveInlining)] + get => _array4; } - readonly EntitiesDB _db; - readonly ExclusiveGroup[] _groups; - - public struct EntityGroupsIterator - { - public EntityGroupsIterator(EntitiesDB db, ExclusiveGroup[] groups) : this() - { - _db = db; - _groups = groups; - _indexGroup = -1; - _index = -1; - } - - public bool MoveNext() - { - //attention, the while is necessary to skip empty groups - while (_index + 1 >= _array1.count && ++_indexGroup < _groups.Length) - { - _index = -1; - _array1 = _db.QueryEntities(_groups[_indexGroup]); - } - - return ++_index < _array1.count; - } - - public void Reset() - { - _index = -1; - _indexGroup = -1; - - _array1 = _db.QueryEntities(_groups[0]); - } - - public ValueRef Current - { - get - { - var valueRef = - new ValueRef(_array1, (uint) _index); - return valueRef; - } - } - - readonly EntitiesDB _db; - readonly ExclusiveGroup[] _groups; - int _index; - int _indexGroup; + public uint count => _array1.count; - EntityCollection _array1; - } + readonly EntityCollection _array1; + readonly EntityCollection _array2; + readonly EntityCollection _array3; + readonly EntityCollection _array4; } - - public struct EntityCollections - where T1 : struct, IEntityComponent where T2 : struct, IEntityComponent where T3 : struct, IEntityComponent - { - public EntityCollections(EntitiesDB db, ExclusiveGroup[] groups) : this() - { - _db = db; - _groups = groups; - } - - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public EntityGroupsIterator GetEnumerator() - { - return new EntityGroupsIterator(_db, _groups); - } - - readonly EntitiesDB _db; - readonly ExclusiveGroup[] _groups; - - public struct EntityGroupsIterator - { - public EntityGroupsIterator(EntitiesDB db, ExclusiveGroup[] groups) : this() - { - _db = db; - _groups = groups; - _indexGroup = -1; - _index = -1; - } - - public bool MoveNext() - { - //attention, the while is necessary to skip empty groups - while (_index + 1 >= _count && ++_indexGroup < _groups.Length) - { - _index = -1; - _array1 = _db.QueryEntities(_groups[_indexGroup]); - _count = _array1.count; - - } - - return ++_index < _count; - } - - public void Reset() - { - _index = -1; - _indexGroup = -1; - - _array1 = _db.QueryEntities(_groups[0]); - _count = _array1.count; - } - - public ValueRef Current - { - get - { - var valueRef = - new ValueRef(_array1, (uint) _index); - return valueRef; - } - } - - readonly EntitiesDB _db; - readonly ExclusiveGroup[] _groups; - uint _count; - int _index; - int _indexGroup; - EntityCollection _array1; - } - } - - public readonly struct BT : IDisposable where BufferT1 : IDisposable - where BufferT2 : IDisposable - where BufferT3 : IDisposable - where BufferT4 : IDisposable + public readonly struct BT { public readonly BufferT1 buffer1; public readonly BufferT2 buffer2; public readonly BufferT3 buffer3; public readonly BufferT4 buffer4; - public readonly uint count; + public readonly int count; public BT(BufferT1 bufferT1, BufferT2 bufferT2, BufferT3 bufferT3, BufferT4 bufferT4, uint count) : this() { @@ -444,65 +240,76 @@ namespace Svelto.ECS this.buffer2 = bufferT2; this.buffer3 = bufferT3; this.buffer4 = bufferT4; - this.count = count; - } - - public void Dispose() - { - buffer1.Dispose(); - buffer2.Dispose(); - buffer3.Dispose(); - buffer4.Dispose(); + this.count = (int) count; } } - public readonly struct BT : IDisposable where BufferT1 : IDisposable - where BufferT2 : IDisposable - where BufferT3 : IDisposable + public readonly struct BT { public readonly BufferT1 buffer1; public readonly BufferT2 buffer2; public readonly BufferT3 buffer3; - public readonly uint count; + public readonly int count; public BT(BufferT1 bufferT1, BufferT2 bufferT2, BufferT3 bufferT3, uint count) : this() { this.buffer1 = bufferT1; this.buffer2 = bufferT2; this.buffer3 = bufferT3; - this.count = count; + this.count = (int) count; } - public void Dispose() + public void Deconstruct(out BufferT1 bufferT1, out BufferT2 bufferT2, out BufferT3 bufferT3, out int count) + { + bufferT1 = buffer1; + bufferT2 = buffer2; + bufferT3 = buffer3; + count = this.count; + } + } + + public readonly struct BT + { + public readonly BufferT1 buffer; + public readonly int count; + + public BT(BufferT1 bufferT1, uint count) : this() + { + this.buffer = bufferT1; + this.count = (int) count; + } + + public void Deconstruct(out BufferT1 bufferT1, out int count) { - buffer1.Dispose(); - buffer2.Dispose(); - buffer3.Dispose(); + bufferT1 = buffer; + count = this.count; } + + public static implicit operator BufferT1(BT t) => t.buffer; } - public readonly struct BT : IDisposable - where BufferT1 : IDisposable where BufferT2 : IDisposable + public readonly struct BT { public readonly BufferT1 buffer1; public readonly BufferT2 buffer2; - public readonly uint count; + public readonly int count; public BT(BufferT1 bufferT1, BufferT2 bufferT2, uint count) : this() { this.buffer1 = bufferT1; this.buffer2 = bufferT2; - this.count = count; + this.count = (int) count; } - - public void Dispose() + + public void Deconstruct(out BufferT1 bufferT1, out BufferT2 bufferT2, out int count) { - buffer1.Dispose(); - buffer2.Dispose(); + bufferT1 = buffer1; + bufferT2 = buffer2; + count = this.count; } } - public ref struct ValueRef where T2 : IEntityComponent where T1 : IEntityComponent + public readonly ref struct ValueRef where T2 : struct, IEntityComponent where T1 : struct, IEntityComponent { readonly EntityCollection array1; @@ -511,7 +318,7 @@ namespace Svelto.ECS public ValueRef(in EntityCollection entity2, uint i) { array1 = entity2; - index = i; + index = i; } public ref T1 entityComponentA @@ -527,8 +334,9 @@ namespace Svelto.ECS } } - public ref struct ValueRef - where T2 : IEntityComponent where T1 : IEntityComponent where T3 : IEntityComponent + public readonly ref struct ValueRef where T2 : struct, IEntityComponent + where T1 : struct, IEntityComponent + where T3 : struct, IEntityComponent { readonly EntityCollection array1; @@ -551,11 +359,11 @@ namespace Svelto.ECS [MethodImpl(MethodImplOptions.AggressiveInlining)] get => ref array1.Item2[index]; } - + public ref T3 entityComponentC { [MethodImpl(MethodImplOptions.AggressiveInlining)] get => ref array1.Item3[index]; } } -} \ No newline at end of file +} diff --git a/Svelto.ECS/EntityCollections.cs b/Svelto.ECS/EntityCollections.cs new file mode 100644 index 0000000..28e7161 --- /dev/null +++ b/Svelto.ECS/EntityCollections.cs @@ -0,0 +1,266 @@ +using System; +using System.Runtime.CompilerServices; +using Svelto.DataStructures; + +namespace Svelto.ECS +{ + public readonly ref struct EntityCollections where T1 : struct, IEntityComponent + where T2 : struct, IEntityComponent + where T3 : struct, IEntityComponent + where T4 : struct, IEntityComponent + { + public EntityCollections(EntitiesDB db, in LocalFasterReadOnlyList groups) : this() + { + _db = db; + _groups = groups; + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public EntityGroupsIterator GetEnumerator() + { + throw new NotImplementedException("tell seb to finish this one"); +#pragma warning disable 162 + return new EntityGroupsIterator(_db, _groups); +#pragma warning restore 162 + } + + readonly EntitiesDB _db; + readonly LocalFasterReadOnlyList _groups; + + public ref struct EntityGroupsIterator + { + public EntityGroupsIterator(EntitiesDB db, in LocalFasterReadOnlyList groups) : this() + { + _db = db; + _groups = groups; + _indexGroup = -1; + _index = -1; + } + + public bool MoveNext() + { + //attention, the while is necessary to skip empty groups + while (_index + 1 >= _count && ++_indexGroup < _groups.count) + { + _index = -1; + _array1 = _db.QueryEntities(_groups[_indexGroup]); + _count = _array1.count; + } + + return ++_index < _count; + } + + public void Reset() + { + _index = -1; + _indexGroup = -1; + + _array1 = _db.QueryEntities(_groups[0]); + _count = _array1.count; + } + + public ValueRef Current + { + get + { + var valueRef = new ValueRef(_array1, (uint) _index); + return valueRef; + } + } + + readonly EntitiesDB _db; + readonly LocalFasterReadOnlyList _groups; + uint _count; + int _index; + int _indexGroup; + + EntityCollection _array1; + } + } + + public readonly ref struct EntityCollections where T1 : struct, IEntityComponent + where T2 : struct, IEntityComponent + where T3 : struct, IEntityComponent + { + public EntityCollections(EntitiesDB db, in LocalFasterReadOnlyList groups) : this() + { + _db = db; + _groups = groups; + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public EntityGroupsIterator GetEnumerator() { return new EntityGroupsIterator(_db, _groups); } + + readonly EntitiesDB _db; + readonly LocalFasterReadOnlyList _groups; + + public ref struct EntityGroupsIterator + { + public EntityGroupsIterator(EntitiesDB db, in LocalFasterReadOnlyList groups) : this() + { + _db = db; + _groups = groups; + _indexGroup = -1; + _index = -1; + } + + public bool MoveNext() + { + //attention, the while is necessary to skip empty groups + while (_index + 1 >= _count && ++_indexGroup < _groups.count) + { + _index = -1; + _array1 = _db.QueryEntities(_groups[_indexGroup]); + _count = _array1.count; + } + + return ++_index < _count; + } + + public void Reset() + { + _index = -1; + _indexGroup = -1; + + _array1 = _db.QueryEntities(_groups[0]); + _count = _array1.count; + } + + public ValueRef Current + { + get + { + var valueRef = new ValueRef(_array1, (uint) _index); + return valueRef; + } + } + + readonly EntitiesDB _db; + readonly LocalFasterReadOnlyList _groups; + uint _count; + int _index; + int _indexGroup; + + EntityCollection _array1; + } + } + + public readonly ref struct EntityCollections + where T1 : struct, IEntityComponent where T2 : struct, IEntityComponent + { + public EntityCollections(EntitiesDB db, in LocalFasterReadOnlyList groups) : this() + { + _db = db; + _groups = groups; + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public EntityGroupsIterator GetEnumerator() { return new EntityGroupsIterator(_db, _groups); } + + readonly EntitiesDB _db; + readonly LocalFasterReadOnlyList _groups; + + public ref struct EntityGroupsIterator + { + public EntityGroupsIterator(EntitiesDB db, in LocalFasterReadOnlyList groups) : this() + { + _db = db; + _groups = groups; + _indexGroup = -1; + _index = -1; + } + + public bool MoveNext() + { + //attention, the while is necessary to skip empty groups + while (_index + 1 >= _array1.count && ++_indexGroup < _groups.count) + { + _index = -1; + _array1 = _db.QueryEntities(_groups[_indexGroup]); + } + + return ++_index < _array1.count; + } + + public void Reset() + { + _index = -1; + _indexGroup = -1; + + _array1 = _db.QueryEntities(_groups[0]); + } + + public ValueRef Current + { + get + { + var valueRef = new ValueRef(_array1, (uint) _index); + return valueRef; + } + } + + readonly EntitiesDB _db; + readonly LocalFasterReadOnlyList _groups; + int _index; + int _indexGroup; + + EntityCollection _array1; + } + } + + public readonly ref struct EntityCollections where T : struct, IEntityComponent + { + public EntityCollections(EntitiesDB db, in LocalFasterReadOnlyList groups) : this() + { + _db = db; + _groups = groups; + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public EntityGroupsIterator GetEnumerator() { return new EntityGroupsIterator(_db, _groups); } + + readonly EntitiesDB _db; + readonly LocalFasterReadOnlyList _groups; + + public ref struct EntityGroupsIterator + { + public EntityGroupsIterator(EntitiesDB db, in LocalFasterReadOnlyList groups) : this() + { + _db = db; + _groups = groups; + _indexGroup = -1; + _index = -1; + } + + public bool MoveNext() + { + //attention, the while is necessary to skip empty groups + while (_index + 1 >= _count && ++_indexGroup < _groups.count) + { + _index = -1; + _array = _db.QueryEntities(_groups[_indexGroup]); + _count = _array.count; + } + + return ++_index < _count; + } + + public void Reset() + { + _index = -1; + _indexGroup = -1; + _count = 0; + } + + public ref T Current => ref _array[(uint) _index]; + + readonly EntitiesDB _db; + readonly LocalFasterReadOnlyList _groups; + + EntityCollection _array; + uint _count; + int _index; + int _indexGroup; + } + } +} \ No newline at end of file diff --git a/Svelto.ECS/EntityComponentInitializer.cs b/Svelto.ECS/EntityComponentInitializer.cs index cadba63..9b7caa6 100644 --- a/Svelto.ECS/EntityComponentInitializer.cs +++ b/Svelto.ECS/EntityComponentInitializer.cs @@ -12,6 +12,8 @@ namespace Svelto.ECS _ID = id; } + public EGID EGID => _ID; + public void Init(T initializer) where T : struct, IEntityComponent { if (_group.TryGetValue(new RefWrapper(ComponentBuilder.ENTITY_COMPONENT_TYPE), @@ -23,7 +25,7 @@ namespace Svelto.ECS SetEGIDWithoutBoxing.SetIDWithoutBoxing(ref initializer, _ID); if (dictionary.TryFindIndex(_ID.entityID, out var findElementIndex)) - dictionary.unsafeValues[findElementIndex] = initializer; + dictionary.GetDirectValueByRef(findElementIndex) = initializer; } public ref T GetOrCreate() where T : struct, IEntityComponent diff --git a/Svelto.ECS/EntityFactory.cs b/Svelto.ECS/EntityFactory.cs index 281b396..088f743 100644 --- a/Svelto.ECS/EntityFactory.cs +++ b/Svelto.ECS/EntityFactory.cs @@ -37,16 +37,16 @@ namespace Svelto.ECS.Internal static void BuildEntitiesAndAddToGroup(EGID entityID, FasterDictionary, ITypeSafeDictionary> group, - IComponentBuilder[] entityBuilders, IEnumerable implementors) + IComponentBuilder[] componentBuilders, IEnumerable implementors) { + var count = componentBuilders.Length; + #if DEBUG && !PROFILE_SVELTO HashSet types = new HashSet(); -#endif - var count = entityBuilders.Length; -#if DEBUG && !PROFILE_SVELTO + for (var index = 0; index < count; ++index) { - var entityComponentType = entityBuilders[index].GetEntityComponentType(); + var entityComponentType = componentBuilders[index].GetEntityComponentType(); if (types.Contains(entityComponentType)) { throw new ECSException("EntityBuilders must be unique inside an EntityDescriptor"); @@ -57,7 +57,7 @@ namespace Svelto.ECS.Internal #endif for (var index = 0; index < count; ++index) { - var entityComponentBuilder = entityBuilders[index]; + var entityComponentBuilder = componentBuilders[index]; var entityComponentType = entityComponentBuilder.GetEntityComponentType(); BuildEntity(entityID, @group, entityComponentType, entityComponentBuilder, implementors); diff --git a/Svelto.ECS/EntityHierarchyStruct.cs b/Svelto.ECS/EntityHierarchyStruct.cs new file mode 100644 index 0000000..5ef8637 --- /dev/null +++ b/Svelto.ECS/EntityHierarchyStruct.cs @@ -0,0 +1,11 @@ +namespace Svelto.ECS +{ + public struct EntityHierarchyStruct: IEntityComponent, INeedEGID + { + public readonly ExclusiveGroupStruct parentGroup; + + public EntityHierarchyStruct(ExclusiveGroup @group): this() { parentGroup = group; } + + public EGID ID { get; set; } + } +} \ No newline at end of file diff --git a/Svelto.ECS/EntityInfoView.cs b/Svelto.ECS/EntityInfoView.cs index 0fe10b1..40eed5c 100644 --- a/Svelto.ECS/EntityInfoView.cs +++ b/Svelto.ECS/EntityInfoView.cs @@ -1,6 +1,6 @@ namespace Svelto.ECS { - struct EntityInfoComponentView: IEntityComponent + struct EntityInfoViewComponent: IEntityComponent { public IComponentBuilder[] componentsToBuild; } diff --git a/Svelto.ECS/EntityNotFoundException.cs b/Svelto.ECS/EntityNotFoundException.cs index e2e4fda..576b536 100644 --- a/Svelto.ECS/EntityNotFoundException.cs +++ b/Svelto.ECS/EntityNotFoundException.cs @@ -5,7 +5,7 @@ namespace Svelto.ECS public class EntityNotFoundException : Exception { public EntityNotFoundException(EGID entityEGID, Type entityType) : base( - $"entity of type '{entityType}' with ID '{entityEGID.entityID}', group '{(uint) entityEGID.groupID}' not found!") + $"entity of type '{entityType}' with ID '{entityEGID.entityID}', group '{entityEGID.groupID.ToName()}' not found!") { } } diff --git a/Svelto.ECS/EntityStream.cs b/Svelto.ECS/EntityStream.cs index c4bf033..aa9181d 100644 --- a/Svelto.ECS/EntityStream.cs +++ b/Svelto.ECS/EntityStream.cs @@ -5,12 +5,11 @@ using Svelto.DataStructures; namespace Svelto.ECS { /// - /// Do not use this class in place of a normal polling. - /// I eventually realised than in ECS no form of communication other than polling entity components can exist. - /// Using groups, you can have always an optimal set of entity components to poll, so EntityStreams must be used - /// only if: - /// - you want to polling engine to be able to track all the entity changes happening in between polls and not - /// just the current state + /// I eventually realised that, with the ECS design, no form of communication other than polling entity components can exist. + /// Using groups, you can have always an optimal set of entity components to poll. However EntityStreams + /// can be useful if: + /// - you need to react on seldom entity changes, usually due to user events + /// - you want engines to be able to track entity changes /// - you want a thread-safe way to read entity states, which includes all the state changes and not the last /// one only /// - you want to communicate between EnginesRoots @@ -26,7 +25,7 @@ namespace Svelto.ECS return (_streams[TypeRefWrapper.wrapper] as EntityStream).GenerateConsumer(name, capacity); } - public Consumer GenerateConsumer(ExclusiveGroup group, string name, uint capacity) + public Consumer GenerateConsumer(ExclusiveGroupStruct group, string name, uint capacity) where T : unmanaged, IEntityComponent { if (_streams.ContainsKey(TypeRefWrapper.wrapper) == false) @@ -107,7 +106,7 @@ namespace Svelto.ECS return consumer; } - internal Consumer GenerateConsumer(ExclusiveGroup group, string name, uint capacity) + internal Consumer GenerateConsumer(ExclusiveGroupStruct group, string name, uint capacity) { var consumer = new Consumer(group, name, capacity); @@ -140,7 +139,7 @@ namespace Svelto.ECS } } - internal Consumer(ExclusiveGroup group, string name, uint capacity) : this(name, capacity) + internal Consumer(ExclusiveGroupStruct group, string name, uint capacity) : this(name, capacity) { this.@group = @group; hasGroup = true; @@ -195,7 +194,7 @@ namespace Svelto.ECS readonly RingBuffer> _ringBuffer; - internal readonly ExclusiveGroup @group; + internal readonly ExclusiveGroupStruct @group; internal readonly bool hasGroup; internal IntPtr mustBeDisposed; diff --git a/Svelto.ECS/EntitySubmissionScheduler.cs b/Svelto.ECS/EntitySubmissionScheduler.cs index d9997db..098cbab 100644 --- a/Svelto.ECS/EntitySubmissionScheduler.cs +++ b/Svelto.ECS/EntitySubmissionScheduler.cs @@ -5,5 +5,7 @@ namespace Svelto.ECS.Schedulers public interface IEntitiesSubmissionScheduler: IDisposable { EnginesRoot.EntitiesSubmitter onTick { set; } + + bool paused { get; set; } } } \ No newline at end of file diff --git a/Svelto.ECS/EntityViewUtility.cs b/Svelto.ECS/EntityViewUtility.cs index e8120b4..057c43d 100644 --- a/Svelto.ECS/EntityViewUtility.cs +++ b/Svelto.ECS/EntityViewUtility.cs @@ -48,38 +48,42 @@ namespace Svelto.ECS entityComponentBlazingFastReflection, out var count); //todo this should happen once per T, not once per Build - foreach (var implementor in implementors) + if (implementors != null) { - if (implementor != null) + foreach (var implementor in implementors) { - var type = implementor.GetType(); + if (implementor != null) + { + var type = implementor.GetType(); - if (cachedTypeInterfaces.TryGetValue(type, out var interfaces) == false) - interfaces = cachedTypeInterfaces[type] = type.GetInterfacesEx(); + if (cachedTypeInterfaces.TryGetValue(type, out var interfaces) == false) + interfaces = cachedTypeInterfaces[type] = type.GetInterfacesEx(); - for (var iindex = 0; iindex < interfaces.Length; iindex++) - { - var componentType = interfaces[iindex]; -#if DEBUG && !PROFILE_SVELTO - if (implementorsByType.TryGetValue(componentType, out var implementorData)) + for (var iindex = 0; iindex < interfaces.Length; iindex++) { - implementorData.numberOfImplementations++; - implementorsByType[componentType] = implementorData; - } - else - implementorsByType[componentType] = new ECSTuple(implementor, 1); + var componentType = interfaces[iindex]; +#if DEBUG && !PROFILE_SVELTO + if (implementorsByType.TryGetValue(componentType, out var implementorData)) + { + implementorData.numberOfImplementations++; + implementorsByType[componentType] = implementorData; + } + else + implementorsByType[componentType] = new ECSTuple(implementor, 1); #else implementorsByType[componentType] = implementor; #endif + } } - } #if DEBUG && !PROFILE_SVELTO - else - { - Console.Log(NULL_IMPLEMENTOR_ERROR.FastConcat(" entityComponent ", - componentBuilder.GetEntityComponentType().ToString())); - } + else + { + Console.Log(NULL_IMPLEMENTOR_ERROR.FastConcat(" entityComponent " + , componentBuilder + .GetEntityComponentType().ToString())); + } #endif + } } for (var i = 0; i < count; i++) diff --git a/Svelto.ECS/ExclusiveGroup.cs b/Svelto.ECS/ExclusiveGroup.cs index e3c0487..862ad28 100644 --- a/Svelto.ECS/ExclusiveGroup.cs +++ b/Svelto.ECS/ExclusiveGroup.cs @@ -1,27 +1,11 @@ using System; using System.Collections.Generic; -using Svelto.ECS.Internal; +using System.Runtime.CompilerServices; #pragma warning disable 660,661 namespace Svelto.ECS { - /// - /// still experimental alternative to ExclusiveGroup, use this like: - /// use this like: - /// public class TriggersGroup : ExclusiveGroup {} - /// - /// - public abstract class NamedExclusiveGroup - { - 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) {} - } - /// /// Exclusive Groups guarantee that the GroupID is unique. /// diff --git a/Svelto.ECS/ExclusiveGroupStruct.cs b/Svelto.ECS/ExclusiveGroupStruct.cs index e683dd4..c87d0f2 100644 --- a/Svelto.ECS/ExclusiveGroupStruct.cs +++ b/Svelto.ECS/ExclusiveGroupStruct.cs @@ -1,7 +1,7 @@ using System; using System.Collections.Generic; +using System.Diagnostics; using System.Runtime.InteropServices; -using Svelto.ECS.Internal; namespace Svelto.ECS { @@ -51,7 +51,7 @@ namespace Svelto.ECS public override string ToString() { - return _id.ToString(); + return this.ToName(); } internal static ExclusiveGroupStruct Generate(byte bitmask = 0) @@ -109,6 +109,8 @@ namespace Svelto.ECS [FieldOffset(0)] uint _id; [FieldOffset(3)] byte _bytemask; - static uint _globalId; + + static uint _globalId = 1; //it starts from 1 because default EGID is considered not initalized value + } } \ No newline at end of file diff --git a/Svelto.ECS/Extensions/ProcessorCount.cs b/Svelto.ECS/Extensions/ProcessorCount.cs index 3f89ac2..56f7ca8 100644 --- a/Svelto.ECS/Extensions/ProcessorCount.cs +++ b/Svelto.ECS/Extensions/ProcessorCount.cs @@ -10,8 +10,8 @@ namespace Svelto.ECS { var iterationsPerBatch = totalIterations / processorCount; - if (iterationsPerBatch < 16) - return 16; + if (iterationsPerBatch < 32) + return 32; return (int) iterationsPerBatch; } diff --git a/Svelto.ECS/Extensions/Svelto/NativeAllGroupsEnumerable.cs b/Svelto.ECS/Extensions/Svelto/AllGroupsEnumerable.cs similarity index 68% rename from Svelto.ECS/Extensions/Svelto/NativeAllGroupsEnumerable.cs rename to Svelto.ECS/Extensions/Svelto/AllGroupsEnumerable.cs index 59a729e..24e90fa 100644 --- a/Svelto.ECS/Extensions/Svelto/NativeAllGroupsEnumerable.cs +++ b/Svelto.ECS/Extensions/Svelto/AllGroupsEnumerable.cs @@ -1,32 +1,37 @@ -using System; using Svelto.DataStructures; using Svelto.ECS.Internal; namespace Svelto.ECS { - public readonly struct NativeAllGroupsEnumerable where T1 : unmanaged, IEntityComponent + /// + /// ToDo it would be interesting to have a version of this dedicated to unmanaged, IEntityComponent + /// that can be burstifiable + /// + /// + public readonly struct AllGroupsEnumerable where T1 : struct, IEntityComponent { - public NativeAllGroupsEnumerable(EntitiesDB db) + public ref struct GroupCollection + { + internal EntityCollection collection; + internal ExclusiveGroupStruct group; + + public void Deconstruct(out EntityCollection collection, out ExclusiveGroupStruct group) + { + collection = this.collection; + group = this.@group; + } + } + + public AllGroupsEnumerable(EntitiesDB db) { _db = db; } - public struct NativeGroupsIterator + public ref struct GroupsIterator { - public struct CurrentGroup: IDisposable - { - public NB buffer; - public ExclusiveGroupStruct group; - - public void Dispose() - { - buffer.Dispose(); - } - } - - public NativeGroupsIterator(EntitiesDB db) : this() + public GroupsIterator(EntitiesDB db) : this() { - _db = db.FindGroups().GetEnumerator(); + _db = db.FindGroups_INTERNAL().GetEnumerator(); } public bool MoveNext() @@ -35,13 +40,11 @@ namespace Svelto.ECS while (_db.MoveNext() == true) { FasterDictionary.KeyValuePairFast group = _db.Current; - ITypeSafeDictionary typeSafeDictionary = @group.Value as ITypeSafeDictionary; - if (typeSafeDictionary.Count == 0) continue; - - _array.buffer = new EntityCollection(typeSafeDictionary.GetValuesArray(out var count), count) - .ToNativeBuffer(); + if (typeSafeDictionary.count == 0) continue; + + _array.collection = new EntityCollection(typeSafeDictionary.GetValues(out var count), count); _array.@group = new ExclusiveGroupStruct(group.Key); return true; @@ -50,19 +53,15 @@ namespace Svelto.ECS return false; } - public void Reset() - { - } - - public CurrentGroup Current => _array; + public GroupCollection Current => _array; - FasterDictionary.FasterDictionaryKeyValueEnumerator _db; - CurrentGroup _array; + FasterDictionary.FasterDictionaryKeyValueEnumerator _db; + GroupCollection _array; } - public NativeGroupsIterator GetEnumerator() + public GroupsIterator GetEnumerator() { - return new NativeGroupsIterator(_db); + return new GroupsIterator(_db); } readonly EntitiesDB _db; @@ -99,7 +98,7 @@ namespace Svelto.ECS if (typeSafeDictionary1.Count == 0) continue; _array = new BT, NB>()(new EntityCollection(typeSafeDictionary1.GetValuesArray(out var count), count) - .ToNativeBuffer(); + .ToBuffer(); return true; } diff --git a/Svelto.ECS/Extensions/Svelto/EntityCollectionExtension.cs b/Svelto.ECS/Extensions/Svelto/EntityCollectionExtension.cs new file mode 100644 index 0000000..17851d4 --- /dev/null +++ b/Svelto.ECS/Extensions/Svelto/EntityCollectionExtension.cs @@ -0,0 +1,162 @@ +using System; +using System.Runtime.CompilerServices; +using Svelto.DataStructures; +using Svelto.ECS.Hybrid; + +namespace Svelto.ECS +{ + public static class EntityCollectionExtension + { + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static void Deconstruct(in this EntityCollection ec, out NB buffer, out int count) where T1 : unmanaged, IEntityComponent + { + buffer = ec._nativedBuffer; + count = (int) ec.count; + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static void Deconstruct(in this EntityCollection ec, out NB buffer1, out NB buffer2, out int count) where T1 : unmanaged, IEntityComponent + where T2 : unmanaged, IEntityComponent + { + buffer1 = ec.Item1._nativedBuffer; + buffer2 = ec.Item2._nativedBuffer; + count = (int) ec.count; + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static void Deconstruct(in this EntityCollection ec, out NB buffer1, out NB buffer2, out NB buffer3, out int count) where T1 : unmanaged, IEntityComponent + where T2 : unmanaged, IEntityComponent + where T3 : unmanaged, IEntityComponent + { + buffer1 = ec.Item1._nativedBuffer; + buffer2 = ec.Item2._nativedBuffer; + buffer3 = ec.Item3._nativedBuffer; + count = (int) ec.count; + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static BT> ToBuffer(in this EntityCollection ec) where T1 : unmanaged, IEntityComponent + { + return new BT>(ec._nativedBuffer, ec.count); + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static BT, NB> ToBuffers + (in this EntityCollection ec) + where T2 : unmanaged, IEntityComponent where T1 : unmanaged, IEntityComponent + { + return new BT, NB>(ec.Item1._nativedBuffer, ec.Item2._nativedBuffer, ec.count); + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static BT, NB, NB> ToBuffers + (in this EntityCollection ec) + where T2 : unmanaged, IEntityComponent + where T1 : unmanaged, IEntityComponent + where T3 : unmanaged, IEntityComponent + { + return new BT, NB, NB>(ec.Item1._nativedBuffer, ec.Item2._nativedBuffer + , ec.Item3._nativedBuffer, ec.count); + } + } + + public static class EntityCollectionExtensionB + { + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static void Deconstruct(in this EntityCollection ec, out MB buffer, out int count) where T1 : struct, IEntityViewComponent + { + buffer = ec._managedBuffer; + count = (int) ec.count; + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static BT> ToBuffer(in this EntityCollection ec) where T1 : struct, IEntityViewComponent + { + return new BT>(ec._managedBuffer, ec.count); + } + + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static (MB buffer1, MB buffer2, uint count) ToBuffers + (in this EntityCollection ec) + where T2 : struct, IEntityViewComponent where T1 : struct, IEntityViewComponent + { + return (ec.Item1._managedBuffer, ec.Item2._managedBuffer, ec.count); + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static (MB buffer1, MB buffer2, MB buffer3, uint count) ToBuffers + (in this EntityCollection ec) + where T2 : struct, IEntityViewComponent + where T1 : struct, IEntityViewComponent + where T3 : struct, IEntityViewComponent + { + return (ec.Item1._managedBuffer, ec.Item2._managedBuffer, ec.Item3._managedBuffer, ec.count); + } + } + + public static class EntityCollectionExtensionC + { + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static (NB buffer1, MB buffer2, uint count) ToBuffers + (in this EntityCollection ec) + where T1 : unmanaged, IEntityComponent where T2 : struct, IEntityViewComponent + { + return (ec.Item1._nativedBuffer, ec.Item2._managedBuffer, ec.count); + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static void Deconstruct(in this EntityCollection ec, out NB buffer1, out MB buffer2, out int count) where T1 : unmanaged, IEntityComponent + where T2 : struct, IEntityViewComponent + { + buffer1 = ec.Item1._nativedBuffer; + buffer2 = ec.Item2._managedBuffer; + count = (int) ec.count; + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static (NB buffer1, MB buffer2, MB buffer3, uint count) ToBuffers + (in this EntityCollection ec) + where T1 : unmanaged, IEntityComponent + where T2 : struct, IEntityViewComponent + where T3 : struct, IEntityViewComponent + { + return (ec.Item1._nativedBuffer, ec.Item2._managedBuffer, ec.Item3._managedBuffer, ec.count); + } + } + + public static class EntityCollectionExtensionD + { + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static void Deconstruct(in this EntityCollection ec, out NB buffer1, out NB buffer2, out MB buffer3, out int count) where T1 : unmanaged, IEntityComponent + where T2 : unmanaged, IEntityComponent + where T3 : struct, IEntityViewComponent + { + buffer1 = ec.Item1._nativedBuffer; + buffer2 = ec.Item2._nativedBuffer; + buffer3 = ec.Item3._managedBuffer; + count = (int) ec.count; + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static (NB buffer1, NB buffer2, MB buffer3, uint count) ToBuffers + (in this EntityCollection ec) + where T1 : unmanaged, IEntityComponent + where T2 : unmanaged, IEntityComponent + where T3 : struct, IEntityViewComponent + { + return (ec.Item1._nativedBuffer, ec.Item2._nativedBuffer, ec.Item3._managedBuffer, ec.count); + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static BT, NB, NB, NB > ToBuffers + (in this EntityCollection ec) + where T2 : unmanaged, IEntityComponent + where T1 : unmanaged, IEntityComponent + where T3 : unmanaged, IEntityComponent + where T4 : unmanaged, IEntityComponent + { + return new BT, NB, NB, NB>(ec.Item1._nativedBuffer, ec.Item2._nativedBuffer, ec.Item3._nativedBuffer, ec.Item4._nativedBuffer, ec.count); + } + } +} \ No newline at end of file diff --git a/Svelto.ECS/Extensions/Svelto/EntityDBExtensions.cs b/Svelto.ECS/Extensions/Svelto/EntityDBExtensions.cs index 5c4495d..e0ba53f 100644 --- a/Svelto.ECS/Extensions/Svelto/EntityDBExtensions.cs +++ b/Svelto.ECS/Extensions/Svelto/EntityDBExtensions.cs @@ -1,77 +1,85 @@ -using System.Collections.Generic; +using System.Runtime.CompilerServices; using Svelto.DataStructures; +using Svelto.ECS.Internal; namespace Svelto.ECS { public static class EntityDBExtensions { - public static NativeGroupsEnumerable NativeGroupsIterator(this EntitiesDB db, - ExclusiveGroupStruct[] groups) - where T1 : unmanaged, IEntityComponent where T2 : unmanaged, IEntityComponent + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static AllGroupsEnumerable QueryEntities(this EntitiesDB db) + where T1 :struct, IEntityComponent { - return new NativeGroupsEnumerable(db, groups, (uint)groups.Length); - } - - public static NativeGroupsEnumerable NativeGroupsIterator(this EntitiesDB db, - FasterList groups) - where T1 : unmanaged, IEntityComponent where T2 : unmanaged, IEntityComponent - { - return new NativeGroupsEnumerable(db, groups, groups.count); + return new AllGroupsEnumerable(db); } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static NB QueryEntitiesAndIndex(this EntitiesDB entitiesDb, EGID entityGID, out uint index) where T : unmanaged, IEntityComponent + { + if (entitiesDb.QueryEntitiesAndIndexInternal(entityGID, out index, out NB array) == true) + return array; - public static NativeGroupsEnumerable NativeGroupsIterator - (this EntitiesDB db, ExclusiveGroupStruct[] groups) - where T1 : unmanaged, IEntityComponent where T2 : unmanaged, IEntityComponent - where T3 : unmanaged, IEntityComponent - { - return new NativeGroupsEnumerable(db, groups); - } - - public static NativeGroupsEnumerable NativeGroupsIterator - (this EntitiesDB db, ExclusiveGroupStruct[] groups) - where T1 : unmanaged, IEntityComponent where T2 : unmanaged, IEntityComponent - where T3 : unmanaged, IEntityComponent where T4 : unmanaged, IEntityComponent - { - return new NativeGroupsEnumerable(db, groups); - } - - public static NativeGroupsEnumerable NativeGroupsIterator(this EntitiesDB db, ExclusiveGroupStruct[] groups) - where T1 : unmanaged, IEntityComponent - { - return new NativeGroupsEnumerable(db, groups); - } + throw new EntityNotFoundException(entityGID, typeof(T)); + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static NB QueryEntitiesAndIndex(this EntitiesDB entitiesDb, uint id, ExclusiveGroupStruct group, out uint index) where T : unmanaged, IEntityComponent + { + EGID entityGID = new EGID(id, group); + if (entitiesDb.QueryEntitiesAndIndexInternal(entityGID, out index, out NB array) == true) + return array; - public static NativeAllGroupsEnumerable NativeGroupsIterator(this EntitiesDB db) - where T1 : unmanaged, IEntityComponent - { - return new NativeAllGroupsEnumerable(db); - } -#if TO_BE_FINISHED - public static NativeAllGroupsEnumerable NativeGroupsIterator(this EntitiesDB db) - where T1 : unmanaged, IEntityComponent where T2 : unmanaged, IEntityComponent - { - return new NativeAllGroupsEnumerable(db); - } -#endif - public static NB NativeEntitiesBuffer(this EntitiesDB db, ExclusiveGroupStruct @group) - where T : unmanaged, IEntityComponent - { - return db.QueryEntities(group).ToNativeBuffer(); - } - - public static BT, NB> NativeEntitiesBuffer(this EntitiesDB db, ExclusiveGroupStruct @group) - where T1 : unmanaged, IEntityComponent - where T2 : unmanaged, IEntityComponent - { - return db.QueryEntities(group).ToNativeBuffers(); - } + throw new EntityNotFoundException(entityGID, typeof(T)); + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static bool TryQueryEntitiesAndIndex(this EntitiesDB entitiesDb, EGID entityGID, out uint index, out NB array) + where T : unmanaged, IEntityComponent + { + if (entitiesDb.QueryEntitiesAndIndexInternal(entityGID, out index, out array) == true) + return true; + + return false; + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static bool TryQueryEntitiesAndIndex(this EntitiesDB entitiesDb, uint id, ExclusiveGroupStruct group, out uint index, out NB array) + where T : unmanaged, IEntityComponent + { + if (entitiesDb.QueryEntitiesAndIndexInternal(new EGID(id, group), out index, out array) == true) + return true; + + return false; + } - public static BT, NB, NB> NativeEntitiesBuffer(this EntitiesDB db, ExclusiveGroupStruct @group) - where T1 : unmanaged, IEntityComponent - where T2 : unmanaged, IEntityComponent - where T3 : unmanaged, IEntityComponent - { - return db.QueryEntities(group).ToNativeBuffers(); - } + [MethodImpl(MethodImplOptions.AggressiveInlining)] + static bool QueryEntitiesAndIndexInternal(this EntitiesDB entitiesDb, EGID entityGID, out uint index, out NB buffer) where T : unmanaged, IEntityComponent + { + index = 0; + buffer = default; + if (entitiesDb.SafeQueryEntityDictionary(entityGID.groupID, out var safeDictionary) == false) + return false; + + if (safeDictionary.TryFindIndex(entityGID.entityID, out index) == false) + return false; + + buffer = (NB) (safeDictionary as ITypeSafeDictionary).GetValues(out _); + + return true; + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static ref T QueryEntity(this EntitiesDB entitiesDb, EGID entityGID) where T : unmanaged, IEntityComponent + { + var array = entitiesDb.QueryEntitiesAndIndex(entityGID, out var index); + + return ref array[(int) index]; + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static ref T QueryEntity(this EntitiesDB entitiesDb, uint id, ExclusiveGroupStruct group) where T : unmanaged, IEntityComponent + { + return ref entitiesDb.QueryEntity(new EGID(id, group)); + } } } \ No newline at end of file diff --git a/Svelto.ECS/Extensions/Svelto/EntityDBExtensionsB.cs b/Svelto.ECS/Extensions/Svelto/EntityDBExtensionsB.cs new file mode 100644 index 0000000..54cfdd4 --- /dev/null +++ b/Svelto.ECS/Extensions/Svelto/EntityDBExtensionsB.cs @@ -0,0 +1,69 @@ +using System.Runtime.CompilerServices; +using Svelto.DataStructures; +using Svelto.ECS.Hybrid; +using Svelto.ECS.Internal; + +namespace Svelto.ECS +{ + public static class EntityDBExtensionsB + { + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static MB QueryEntitiesAndIndex(this EntitiesDB entitiesDb, EGID entityGID, out uint index) where T : struct, IEntityViewComponent + { + if (entitiesDb.QueryEntitiesAndIndexInternal(entityGID, out index, out MB array) == true) + return array; + + throw new EntityNotFoundException(entityGID, typeof(T)); + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static bool TryQueryEntitiesAndIndex(this EntitiesDB entitiesDb, EGID entityGID, out uint index, out MB array) + where T : struct, IEntityViewComponent + { + if (entitiesDb.QueryEntitiesAndIndexInternal(entityGID, out index, out array) == true) + return true; + + return false; + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static bool TryQueryEntitiesAndIndex(this EntitiesDB entitiesDb, uint id, ExclusiveGroupStruct group, out uint index, out MB array) + where T : struct, IEntityViewComponent + { + if (entitiesDb.QueryEntitiesAndIndexInternal(new EGID(id, group), out index, out array) == true) + return true; + + return false; + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + static bool QueryEntitiesAndIndexInternal(this EntitiesDB entitiesDb, EGID entityGID, out uint index, out MB buffer) where T : struct, IEntityViewComponent + { + index = 0; + buffer = default; + if (entitiesDb.SafeQueryEntityDictionary(entityGID.groupID, out var safeDictionary) == false) + return false; + + if (safeDictionary.TryFindIndex(entityGID.entityID, out index) == false) + return false; + + buffer = (MB) (safeDictionary as ITypeSafeDictionary).GetValues(out _); + + return true; + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static ref T QueryEntity(this EntitiesDB entitiesDb, EGID entityGID) where T : struct, IEntityViewComponent + { + var array = entitiesDb.QueryEntitiesAndIndex(entityGID, out var index); + + return ref array[(int) index]; + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static ref T QueryEntity(this EntitiesDB entitiesDb, uint id, ExclusiveGroupStruct group) where T : struct, IEntityViewComponent + { + return ref entitiesDb.QueryEntity(new EGID(id, group)); + } + } +} \ No newline at end of file diff --git a/Svelto.ECS/Extensions/Svelto/GroupsEnumerable.cs b/Svelto.ECS/Extensions/Svelto/GroupsEnumerable.cs new file mode 100644 index 0000000..854ee57 --- /dev/null +++ b/Svelto.ECS/Extensions/Svelto/GroupsEnumerable.cs @@ -0,0 +1,249 @@ +using DBC.ECS; +using Svelto.DataStructures; + +namespace Svelto.ECS +{ + /// + /// NOTE THESE ENUMERABLES EXIST TO AVOID BOILERPLATE CODE AS THEY SKIP 0 SIZED GROUPS + /// However if the normal pattern with the double foreach is used, this is not necessary + /// Note: atm cannot be ref structs because they are returned in a valuetuple + /// + /// + /// + /// + /// + public readonly ref struct GroupsEnumerable where T1 : struct, IEntityComponent + where T2 : struct, IEntityComponent + where T3 : struct, IEntityComponent + where T4 : struct, IEntityComponent + { + readonly EntitiesDB _db; + readonly LocalFasterReadOnlyList _groups; + + public GroupsEnumerable(EntitiesDB db, in LocalFasterReadOnlyList groups) + { + _db = db; + _groups = groups; + } + + public ref struct GroupsIterator + { + public GroupsIterator(EntitiesDB db, in LocalFasterReadOnlyList groups) : this() + { + _groups = groups; + _indexGroup = -1; + _entitiesDB = db; + } + + public bool MoveNext() + { + //attention, the while is necessary to skip empty groups + while (++_indexGroup < _groups.count) + { + var entityCollection1 = _entitiesDB.QueryEntities(_groups[_indexGroup]); + if (entityCollection1.count == 0) + continue; + var entityCollection2 = _entitiesDB.QueryEntities(_groups[_indexGroup]); + if (entityCollection2.count == 0) + continue; + + Check.Assert(entityCollection1.count == entityCollection2.count + , "congratulation, you found a bug in Svelto, please report it"); + + EntityCollection array = entityCollection1; + var array2 = entityCollection2; + _buffers = new EntityCollection( + array.Item1, array.Item2, array.Item3, array2); + break; + } + + var moveNext = _indexGroup < _groups.count; + + if (moveNext == false) + Reset(); + + return moveNext; + } + + public void Reset() { _indexGroup = -1; } + + public EntityCollection Current => _buffers; + + readonly LocalFasterReadOnlyList _groups; + + int _indexGroup; + EntityCollection _buffers; + readonly EntitiesDB _entitiesDB; + } + + public GroupsIterator GetEnumerator() { return new GroupsIterator(_db, _groups); } + } + + /// + /// ToDo source gen could return the implementation of IBuffer directly, but cannot be done manually + /// + /// + /// + /// + public readonly ref struct GroupsEnumerable where T1 : struct, IEntityComponent + where T2 : struct, IEntityComponent + where T3 : struct, IEntityComponent + { + readonly EntitiesDB _db; + readonly LocalFasterReadOnlyList _groups; + + public GroupsEnumerable(EntitiesDB db, in LocalFasterReadOnlyList groups) + { + _db = db; + _groups = groups; + } + + public ref struct GroupsIterator + { + public GroupsIterator(EntitiesDB db, in LocalFasterReadOnlyList groups) : this() + { + _groups = groups; + _indexGroup = -1; + _entitiesDB = db; + } + + public bool MoveNext() + { + //attention, the while is necessary to skip empty groups + while (++_indexGroup < _groups.count) + { + EntityCollection entityCollection = _entitiesDB.QueryEntities(_groups[_indexGroup]); + if (entityCollection.count == 0) + continue; + + _buffers = entityCollection; + break; + } + + var moveNext = _indexGroup < _groups.count; + + if (moveNext == false) + Reset(); + + return moveNext; + } + + public void Reset() { _indexGroup = -1; } + + public EntityCollection Current => _buffers; + + readonly LocalFasterReadOnlyList _groups; + + int _indexGroup; + EntityCollection _buffers; + readonly EntitiesDB _entitiesDB; + } + + public GroupsIterator GetEnumerator() { return new GroupsIterator(_db, _groups); } + } + + public readonly ref struct GroupsEnumerable where T1 : struct, IEntityComponent where T2 : struct, IEntityComponent + { + public GroupsEnumerable(EntitiesDB db, in LocalFasterReadOnlyList groups) + { + _db = db; + _groups = groups; + } + + public ref struct GroupsIterator + { + public GroupsIterator(EntitiesDB db, in LocalFasterReadOnlyList groups) : this() + { + _db = db; + _groups = groups; + _indexGroup = -1; + } + + public bool MoveNext() + { + //attention, the while is necessary to skip empty groups + while (++_indexGroup < _groups.count) + { + var entityCollection = _db.QueryEntities(_groups[_indexGroup]); + if (entityCollection.count == 0) + continue; + + _buffers = entityCollection; + break; + } + + var moveNext = _indexGroup < _groups.count; + + if (moveNext == false) + Reset(); + + return moveNext; + } + + public void Reset() { _indexGroup = -1; } + + public EntityCollection Current => _buffers; + + readonly EntitiesDB _db; + readonly LocalFasterReadOnlyList _groups; + + int _indexGroup; + EntityCollection _buffers; + } + + public GroupsIterator GetEnumerator() { return new GroupsIterator(_db, _groups); } + + readonly EntitiesDB _db; + readonly LocalFasterReadOnlyList _groups; + } + + public readonly ref struct GroupsEnumerable where T1 : struct, IEntityComponent + { + public GroupsEnumerable(EntitiesDB db, in LocalFasterReadOnlyList groups) + { + _db = db; + _groups = groups; + } + + public ref struct GroupsIterator + { + public GroupsIterator(EntitiesDB db, in LocalFasterReadOnlyList groups) : this() + { + _db = db; + _groups = groups; + _indexGroup = -1; + } + + public bool MoveNext() + { + //attention, the while is necessary to skip empty groups + while (++_indexGroup < _groups.count) + { + var entityCollection = _db.QueryEntities(_groups[_indexGroup]); + if (entityCollection.count == 0) + continue; + + _buffer = entityCollection; + break; + } + + return _indexGroup < _groups.count; + } + + public void Reset() { _indexGroup = -1; } + + public EntityCollection Current => _buffer; + + readonly EntitiesDB _db; + readonly LocalFasterReadOnlyList _groups; + + int _indexGroup; + EntityCollection _buffer; + } + + public GroupsIterator GetEnumerator() { return new GroupsIterator(_db, _groups); } + + readonly EntitiesDB _db; + readonly LocalFasterReadOnlyList _groups; + } +} \ No newline at end of file diff --git a/Svelto.ECS/Extensions/Svelto/NativeGroupsEnumerable.cs b/Svelto.ECS/Extensions/Svelto/NativeGroupsEnumerable.cs deleted file mode 100644 index e61359d..0000000 --- a/Svelto.ECS/Extensions/Svelto/NativeGroupsEnumerable.cs +++ /dev/null @@ -1,229 +0,0 @@ -using DBC.ECS; -using Svelto.DataStructures; - -namespace Svelto.ECS -{ - public readonly struct NativeGroupsEnumerable where T1 : unmanaged, IEntityComponent - where T2 : unmanaged, IEntityComponent - where T3 : unmanaged, IEntityComponent - where T4 : unmanaged, IEntityComponent - { - readonly EntitiesDB _db; - readonly ExclusiveGroupStruct[] _groups; - - public NativeGroupsEnumerable(EntitiesDB db, ExclusiveGroupStruct[] groups) - { - _db = db; - _groups = groups; - } - - public struct NativeGroupsIterator - { - public NativeGroupsIterator(EntitiesDB db, ExclusiveGroupStruct[] groups) : this() - { - _groups = groups; - _indexGroup = -1; - _entitiesDB = db; - } - - public bool MoveNext() - { - //attention, the while is necessary to skip empty groups - while (++_indexGroup < _groups.Length) - { - var entityCollection1 = _entitiesDB.QueryEntities(_groups[_indexGroup]); - if (entityCollection1.count == 0) - continue; - var entityCollection2 = _entitiesDB.QueryEntities(_groups[_indexGroup]); - if (entityCollection2.count == 0) - continue; - - Check.Assert(entityCollection1.count == entityCollection2.count - , "congratulation, you found a bug in Svelto, please report it"); - - var array = entityCollection1.ToNativeBuffers(); - var array2 = entityCollection2.ToNativeBuffer(); - _array = new BT, NB, NB, NB>(array.buffer1, array.buffer2, array.buffer3, array2 - , entityCollection1.count); - break; - } - - return _indexGroup < _groups.Length; - } - - public void Reset() { _indexGroup = -1; } - - public BT, NB, NB, NB> Current => _array; - - readonly ExclusiveGroupStruct[] _groups; - - int _indexGroup; - BT, NB, NB, NB> _array; - readonly EntitiesDB _entitiesDB; - } - - public NativeGroupsIterator GetEnumerator() { return new NativeGroupsIterator(_db, _groups); } - } - - public readonly struct NativeGroupsEnumerable 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(_groups[_indexGroup]); - if (entityCollection.count == 0) - continue; - - _array = entityCollection.ToNativeBuffers(); - break; - } - - return _indexGroup < _groups.Length; - } - - public void Reset() { _indexGroup = -1; } - - public BT, NB, NB> Current => _array; - - readonly ExclusiveGroupStruct[] _groups; - - int _indexGroup; - BT, NB, NB> _array; - readonly EntitiesDB _entitiesDB; - } - - public NativeGroupsIterator GetEnumerator() { return new NativeGroupsIterator(_db, _groups); } - } - - public struct NativeGroupsEnumerable where T1 : unmanaged, IEntityComponent - where T2 : unmanaged, IEntityComponent - { - public NativeGroupsEnumerable(EntitiesDB db, ExclusiveGroupStruct[] groups, uint groupsLength) - { - _db = db; - _groups = groups; - _groupsLength = groupsLength; - } - - public NativeGroupsEnumerable(EntitiesDB db, FasterList groups, uint groupsLength) - { - _db = db; - _groups = groups.ToArrayFast(out _); - _groupsLength = groupsLength; - } - - public struct NativeGroupsIterator - { - public NativeGroupsIterator(EntitiesDB db, ExclusiveGroupStruct[] groups) : this() - { - _db = db; - _groups = groups; - _indexGroup = -1; - } - - public bool MoveNext() - { - //attention, the while is necessary to skip empty groups - while (++_indexGroup < _groups.Length) - { - var entityCollection = _db.QueryEntities(_groups[_indexGroup]); - if (entityCollection.count == 0) - continue; - - _array = entityCollection.ToNativeBuffers(); - break; - } - - return _indexGroup < _groups.Length; - } - - public void Reset() { _indexGroup = -1; } - - public BT, NB> Current => _array; - - readonly EntitiesDB _db; - readonly ExclusiveGroupStruct[] _groups; - - int _indexGroup; - BT, NB> _array; - } - - public NativeGroupsIterator GetEnumerator() { return new NativeGroupsIterator(_db, _groups); } - - readonly EntitiesDB _db; - readonly ExclusiveGroupStruct[] _groups; - readonly uint _groupsLength; - } - - public struct NativeGroupsEnumerable 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(_groups[_indexGroup]); - if (entityCollection.count == 0) - continue; - - _array = entityCollection.ToNativeBuffer(); - break; - } - - return _indexGroup < _groups.Length; - } - - public void Reset() { _indexGroup = -1; } - - public NB Current => _array; - - readonly EntitiesDB _db; - readonly ExclusiveGroupStruct[] _groups; - - int _indexGroup; - NB _array; - } - - public NativeGroupsIterator GetEnumerator() { return new NativeGroupsIterator(_db, _groups); } - - readonly EntitiesDB _db; - readonly ExclusiveGroupStruct[] _groups; - } -} \ No newline at end of file diff --git a/Svelto.ECS/Extensions/Svelto/SortedEnginesGroup.cs b/Svelto.ECS/Extensions/Svelto/SortedEnginesGroup.cs new file mode 100644 index 0000000..8e6cc35 --- /dev/null +++ b/Svelto.ECS/Extensions/Svelto/SortedEnginesGroup.cs @@ -0,0 +1,86 @@ +using Svelto.DataStructures; +using Svelto.Common; + +namespace Svelto.ECS.Extensions +{ + public interface IStepEngine : IEngine + { + void Step(); + + string name { get; } + } + + public interface IGroupEngine : IStepEngine + { } + + public interface IStepEngine : IEngine + { + void Step(ref T _param); + + string name { get; } + } + + public interface IStepGroupEngine : IStepEngine + { + } + /// + /// Note sorted jobs run in serial + /// + /// + /// + public abstract class SortedEnginesGroup : IGroupEngine + where SequenceOrder : struct, ISequenceOrder where Interface : class, IStepEngine + { + protected SortedEnginesGroup(FasterList engines) + { + _name = "SortedEnginesGroup - "+this.GetType().Name; + _instancedSequence = new Sequence(engines); + } + + public void Step() + { + var sequenceItems = _instancedSequence.items; + using (var profiler = new PlatformProfiler(_name)) + { + for (var index = 0; index < sequenceItems.count; index++) + { + var engine = sequenceItems[index]; + using (profiler.Sample(engine.name)) engine.Step(); + } + } + } + + public string name => _name; + + readonly string _name; + readonly Sequence _instancedSequence; + } + + public abstract class SortedEnginesGroup: IStepGroupEngine + where SequenceOrder : struct, ISequenceOrder where Interface : class, IStepEngine + { + protected SortedEnginesGroup(FasterList engines) + { + _name = "SortedEnginesGroup - "+this.GetType().Name; + _instancedSequence = new Sequence(engines); + } + + public void Step(ref Parameter param) + { + var sequenceItems = _instancedSequence.items; + using (var profiler = new PlatformProfiler(_name)) + { + for (var index = 0; index < sequenceItems.count; index++) + { + var engine = sequenceItems[index]; + using (profiler.Sample(engine.name)) engine.Step(ref param); + } + } + } + + public string name => _name; + + readonly string _name; + readonly Sequence _instancedSequence; + } +} diff --git a/Svelto.ECS/Extensions/Unity/DOTS/CopySveltoToUECSEnginesGroup.cs b/Svelto.ECS/Extensions/Unity/DOTS/CopySveltoToUECSEnginesGroup.cs deleted file mode 100644 index 523a04f..0000000 --- a/Svelto.ECS/Extensions/Unity/DOTS/CopySveltoToUECSEnginesGroup.cs +++ /dev/null @@ -1,30 +0,0 @@ -#if UNITY_ECS -using Svelto.Common; -using Unity.Entities; -using Unity.Jobs; - -namespace Svelto.ECS.Extensions.Unity -{ - [Sequenced(nameof(JobifiedSveltoEngines.CopySveltoToUECSEnginesGroup))] - [DisableAutoCreation] - public class CopySveltoToUECSEnginesGroup : ComponentSystemGroup, IJobifiedEngine - { - public JobHandle Execute(JobHandle _jobHandle) - { - foreach (var engine in Systems) - (engine as ICopySveltoToUECSEngine).jobHandle = _jobHandle; - - Update(); - - return _jobHandle; - } - - readonly SimulationSystemGroup _simulationSystemGroup; - } - - public interface ICopySveltoToUECSEngine:IEngine - { - JobHandle jobHandle { set; } - } -} -#endif \ No newline at end of file diff --git a/Svelto.ECS/Extensions/Unity/DOTS/EnginesRoot.NativeOperation.cs b/Svelto.ECS/Extensions/Unity/DOTS/EnginesRoot.NativeOperation.cs index e8f48b2..54dd0de 100644 --- a/Svelto.ECS/Extensions/Unity/DOTS/EnginesRoot.NativeOperation.cs +++ b/Svelto.ECS/Extensions/Unity/DOTS/EnginesRoot.NativeOperation.cs @@ -1,7 +1,7 @@ -#if UNITY_ECS +#if UNITY_BURST +using System; using Svelto.Common; using Svelto.DataStructures; -using Svelto.ECS.DataStructures; using Svelto.ECS.DataStructures.Unity; using Unity.Jobs.LowLevel.Unsafe; @@ -19,30 +19,30 @@ namespace Svelto.ECS readonly AtomicNativeBags _swapOperationQueue = new AtomicNativeBags(Common.Allocator.Persistent, JobsUtility.MaxJobThreadCount + 1); - NativeEntityRemove ProvideNativeEntityRemoveQueue() where T : IEntityDescriptor, new() + NativeEntityRemove ProvideNativeEntityRemoveQueue(string memberName) where T : IEntityDescriptor, new() { //todo: remove operation array and store entity descriptor hash in the return value //todo I maybe able to provide a _nativeSwap.SwapEntity _nativeRemoveOperations.Add( - new NativeOperationRemove(EntityDescriptorTemplate.descriptor.componentsToBuild)); + new NativeOperationRemove(EntityDescriptorTemplate.descriptor.componentsToBuild, TypeCache.type, memberName)); return new NativeEntityRemove(_removeOperationQueue, _nativeRemoveOperations.count - 1); } - NativeEntitySwap ProvideNativeEntitySwapQueue() where T : IEntityDescriptor, new() + NativeEntitySwap ProvideNativeEntitySwapQueue(string memberName) where T : IEntityDescriptor, new() { //todo: remove operation array and store entity descriptor hash in the return value _nativeSwapOperations.Add( - new NativeOperationSwap(EntityDescriptorTemplate.descriptor.componentsToBuild)); + new NativeOperationSwap(EntityDescriptorTemplate.descriptor.componentsToBuild, TypeCache.type, memberName)); return new NativeEntitySwap(_swapOperationQueue, _nativeSwapOperations.count - 1); } - NativeEntityFactory ProvideNativeEntityFactoryQueue() where T : IEntityDescriptor, new() + NativeEntityFactory ProvideNativeEntityFactoryQueue(string memberName) where T : IEntityDescriptor, new() { //todo: remove operation array and store entity descriptor hash in the return value _nativeAddOperations.Add( - new NativeOperationBuild(EntityDescriptorTemplate.descriptor.componentsToBuild)); + new NativeOperationBuild(EntityDescriptorTemplate.descriptor.componentsToBuild, TypeCache.type)); return new NativeEntityFactory(_addOperationQueue, _nativeAddOperations.count - 1); } @@ -59,10 +59,10 @@ namespace Svelto.ECS { var componentsIndex = buffer.Dequeue(); var entityEGID = buffer.Dequeue(); - CheckRemoveEntityID(entityEGID); + CheckRemoveEntityID(entityEGID, _nativeRemoveOperations[componentsIndex].type); QueueEntitySubmitOperation(new EntitySubmitOperation( EntitySubmitOperationType.Remove, entityEGID, entityEGID - , _nativeRemoveOperations[componentsIndex].entityComponents)); + , _nativeRemoveOperations[componentsIndex].components)); } } @@ -75,12 +75,12 @@ namespace Svelto.ECS var componentsIndex = buffer.Dequeue(); var entityEGID = buffer.Dequeue(); - CheckRemoveEntityID(entityEGID.@from); - CheckAddEntityID(entityEGID.to); + CheckRemoveEntityID(entityEGID.@from, _nativeSwapOperations[componentsIndex].type, _nativeSwapOperations[componentsIndex].caller ); + CheckAddEntityID(entityEGID.to, _nativeSwapOperations[componentsIndex].type, _nativeSwapOperations[componentsIndex].caller); QueueEntitySubmitOperation(new EntitySubmitOperation( EntitySubmitOperationType.Swap, entityEGID.@from, entityEGID.to - , _nativeSwapOperations[componentsIndex].entityComponents)); + , _nativeSwapOperations[componentsIndex].components)); } } } @@ -98,8 +98,9 @@ namespace Svelto.ECS var componentCounts = buffer.Dequeue(); EntityComponentInitializer init = - BuildEntity(egid, _nativeAddOperations[componentsIndex].components); + BuildEntity(egid, _nativeAddOperations[componentsIndex].components, _nativeAddOperations[componentsIndex].type); + //only called if Init is called on the initialized (there is something to init) while (componentCounts > 0) { componentCounts--; @@ -140,125 +141,43 @@ namespace Svelto.ECS } } - public readonly struct NativeEntityRemove - { - readonly AtomicNativeBags _removeQueue; - readonly uint _indexRemove; - - internal NativeEntityRemove(AtomicNativeBags EGIDsToRemove, uint indexRemove) - { - _removeQueue = EGIDsToRemove; - _indexRemove = indexRemove; - } - - public void RemoveEntity(EGID egid, int threadIndex) - { - var simpleNativeBag = _removeQueue.GetBuffer(threadIndex); - - simpleNativeBag.Enqueue(_indexRemove); - simpleNativeBag.Enqueue(egid); - } - } - - public readonly struct NativeEntitySwap - { - readonly AtomicNativeBags _swapQueue; - readonly uint _indexSwap; - - internal NativeEntitySwap(AtomicNativeBags EGIDsToSwap, uint indexSwap) - { - _swapQueue = EGIDsToSwap; - _indexSwap = indexSwap; - } - - public void SwapEntity(EGID from, EGID to, int threadIndex) - { - var simpleNativeBag = _swapQueue.GetBuffer(threadIndex); - simpleNativeBag.Enqueue(_indexSwap); - simpleNativeBag.Enqueue(new DoubleEGID(from, to)); - } - - public void SwapEntity(EGID from, ExclusiveGroupStruct to, int threadIndex) - { - var simpleNativeBag = _swapQueue.GetBuffer(threadIndex); - simpleNativeBag.Enqueue(_indexSwap); - simpleNativeBag.Enqueue(new DoubleEGID(from, new EGID(from.entityID, to))); - } - } - - public readonly struct NativeEntityFactory - { - readonly AtomicNativeBags _addOperationQueue; - readonly uint _index; - - internal NativeEntityFactory(AtomicNativeBags addOperationQueue, uint index) - { - _index = index; - _addOperationQueue = addOperationQueue; - } - - public NativeEntityComponentInitializer BuildEntity - (uint eindex, ExclusiveGroupStruct buildGroup, int threadIndex) - { - NativeBag unsafeBuffer = _addOperationQueue.GetBuffer(threadIndex + 1); - - unsafeBuffer.Enqueue(_index); - unsafeBuffer.Enqueue(new EGID(eindex, buildGroup)); - unsafeBuffer.ReserveEnqueue(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(in T component) where T : unmanaged, IEntityComponent - { - uint id = EntityComponentID.ID.Data; - - _unsafeBuffer.AccessReserved(_index)++; - - _unsafeBuffer.Enqueue(id); - _unsafeBuffer.Enqueue(component); - } - } - - struct NativeOperationBuild + readonly struct NativeOperationBuild { internal readonly IComponentBuilder[] components; + internal readonly Type type; - public NativeOperationBuild(IComponentBuilder[] descriptorEntityComponentsToBuild) + public NativeOperationBuild(IComponentBuilder[] descriptorComponentsToBuild, Type entityType) { - components = descriptorEntityComponentsToBuild; + type = entityType; + components = descriptorComponentsToBuild; } } readonly struct NativeOperationRemove { - internal readonly IComponentBuilder[] entityComponents; - - public NativeOperationRemove(IComponentBuilder[] descriptorEntitiesToBuild) + internal readonly IComponentBuilder[] components; + internal readonly Type type; + internal readonly string caller; + + public NativeOperationRemove(IComponentBuilder[] descriptorComponentsToRemove, Type entityType, string caller) { - entityComponents = descriptorEntitiesToBuild; + this.caller = caller; + components = descriptorComponentsToRemove; + type = entityType; } } readonly struct NativeOperationSwap { - internal readonly IComponentBuilder[] entityComponents; + internal readonly IComponentBuilder[] components; + internal readonly Type type; + internal readonly string caller; - public NativeOperationSwap(IComponentBuilder[] descriptorEntitiesToBuild) + public NativeOperationSwap(IComponentBuilder[] descriptorComponentsToSwap, Type entityType, string caller) { - entityComponents = descriptorEntitiesToBuild; + this.caller = caller; + components = descriptorComponentsToSwap; + type = entityType; } } } diff --git a/Svelto.ECS/Extensions/Unity/DOTS/IJobifiedEngine.cs b/Svelto.ECS/Extensions/Unity/DOTS/IJobifiedEngine.cs deleted file mode 100644 index b52863b..0000000 --- a/Svelto.ECS/Extensions/Unity/DOTS/IJobifiedEngine.cs +++ /dev/null @@ -1,9 +0,0 @@ -using Unity.Jobs; - -namespace Svelto.ECS.Extensions.Unity -{ - public interface IJobifiedEngine : IEngine - { - JobHandle Execute(JobHandle _jobHandle); - } -} \ No newline at end of file diff --git a/Svelto.ECS/Extensions/Unity/DOTS/JobifedEnginesGroup.cs b/Svelto.ECS/Extensions/Unity/DOTS/JobifedEnginesGroup.cs index 3566aff..093a4a0 100644 --- a/Svelto.ECS/Extensions/Unity/DOTS/JobifedEnginesGroup.cs +++ b/Svelto.ECS/Extensions/Unity/DOTS/JobifedEnginesGroup.cs @@ -1,31 +1,97 @@ +#if UNITY_2019_2_OR_NEWER +using Svelto.Common; using Svelto.DataStructures; using Unity.Jobs; namespace Svelto.ECS.Extensions.Unity { - public abstract class JobifedEnginesGroup - where Interface : class, IJobifiedEngine + public interface IJobifiedEngine : IEngine { - protected JobifedEnginesGroup(FasterReadOnlyList engines, bool completeEachJob = false) + JobHandle Execute(JobHandle _jobHandle); + + string name { get; } + } + + public interface IJobifiedGroupEngine : IJobifiedEngine + { } + + public interface IJobifiedEngine : IEngine + { + JobHandle Execute(JobHandle _jobHandle, ref T _param); + + string name { get; } + } + + public interface IJobifiedGroupEngine : IJobifiedEngine + { + } + /// + /// Note unsorted jobs run in parallel + /// + /// + public abstract class JobifedEnginesGroup : IJobifiedGroupEngine where Interface : class, IJobifiedEngine + { + protected JobifedEnginesGroup(FasterList engines) { + _name = "JobifiedEnginesGroup - "+this.GetType().Name; _engines = engines; - _completeEachJob = completeEachJob; } - public JobHandle Execute(JobHandle combinedHandles) + public JobHandle Execute(JobHandle inputHandles) { - var fasterReadOnlyList = _engines; - for (var index = 0; index < fasterReadOnlyList.Count; index++) + var engines = _engines; + JobHandle combinedHandles = inputHandles; + using (var profiler = new PlatformProfiler(_name)) { - var engine = fasterReadOnlyList[index]; - combinedHandles = JobHandle.CombineDependencies(combinedHandles, engine.Execute(combinedHandles)); - if (_completeEachJob) combinedHandles.Complete(); + for (var index = 0; index < engines.count; index++) + { + ref var engine = ref engines[index]; + using (profiler.Sample(engine.name)) + { + combinedHandles = JobHandle.CombineDependencies(combinedHandles, engine.Execute(inputHandles)); + } + } } return combinedHandles; } + public string name => _name; + readonly FasterReadOnlyList _engines; readonly bool _completeEachJob; + readonly string _name; + } + + public abstract class JobifedEnginesGroup: IJobifiedGroupEngine where Interface : class, IJobifiedEngine + { + protected JobifedEnginesGroup(FasterList engines) + { + _name = "JobifiedEnginesGroup - "+this.GetType().Name; + _engines = engines; + } + + public JobHandle Execute(JobHandle combinedHandles, ref Param _param) + { + var engines = _engines; + using (var profiler = new PlatformProfiler(_name)) + { + for (var index = 0; index < engines.count; index++) + { + var engine = engines[index]; + using (profiler.Sample(engine.name)) combinedHandles = + JobHandle.CombineDependencies(combinedHandles, engine.Execute(combinedHandles, ref _param)); + } + } + + return combinedHandles; + } + + public string name => _name; + + readonly string _name; + + readonly FasterReadOnlyList _engines; } -} \ No newline at end of file +} +#endif \ No newline at end of file diff --git a/Svelto.ECS/Extensions/Unity/DOTS/JobifiedSveltoEngines.cs b/Svelto.ECS/Extensions/Unity/DOTS/JobifiedSveltoEngines.cs index 98a9ec4..3f67e89 100644 --- a/Svelto.ECS/Extensions/Unity/DOTS/JobifiedSveltoEngines.cs +++ b/Svelto.ECS/Extensions/Unity/DOTS/JobifiedSveltoEngines.cs @@ -1,4 +1,4 @@ -#if UNITY_ECS +#if UNITY_BURST namespace Svelto.ECS.Extensions.Unity { public enum JobifiedSveltoEngines diff --git a/Svelto.ECS/Extensions/Unity/DOTS/NativeEGIDMapper.cs b/Svelto.ECS/Extensions/Unity/DOTS/NativeEGIDMapper.cs new file mode 100644 index 0000000..34d91b6 --- /dev/null +++ b/Svelto.ECS/Extensions/Unity/DOTS/NativeEGIDMapper.cs @@ -0,0 +1,83 @@ +#if UNITY_BURST +using System; +using System.Runtime.CompilerServices; +using Svelto.DataStructures; + +namespace Svelto.ECS +{ + public readonly struct NativeEGIDMapper where T : unmanaged, IEntityComponent + { + readonly SveltoDictionaryNative map; + public ExclusiveGroupStruct groupID { get; } + + public NativeEGIDMapper + (ExclusiveGroupStruct groupStructId + , SveltoDictionary>, NativeStrategy> toNative) : this() + { + groupID = groupStructId; + map = toNative; + } + + public uint Count => map.count; + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public ref T Entity(uint entityID) + { +#if DEBUG && !PROFILE_SVELTO + if (map.TryFindIndex(entityID, out var findIndex) == false) + throw new Exception("Entity not found in this group ".FastConcat(typeof(T).ToString())); +#else + map.TryFindIndex(entityID, out var findIndex); +#endif + return ref map.GetDirectValueByRef(findIndex); + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public bool TryGetEntity(uint entityID, out T value) + { + if (map.count > 0 && map.TryFindIndex(entityID, out var index)) + { + unsafe + { + value = Unsafe.AsRef(Unsafe.Add((void*) map.GetValues(out _).ToNativeArray(out _) + , (int) index)); + return true; + } + } + + value = default; + return false; + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public NB GetArrayAndEntityIndex(uint entityID, out uint index) + { + if (map.TryFindIndex(entityID, out index)) + { + return new NB((IntPtr) map.GetValues(out var count).ToNativeArray(out _), count); + } + + throw new ECSException("Entity not found"); + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public bool TryGetArrayAndEntityIndex(uint entityID, out uint index, out NB array) + { + index = 0; + if (map.count > 0 && map.TryFindIndex(entityID, out index)) + { + array = new NB((IntPtr) map.GetValues(out var count).ToNativeArray(out _), count); + return true; + } + + array = default; + return false; + } + + public bool Exists(uint idEntityId) + { + return map.count > 0 && map.TryFindIndex(idEntityId, out _); + } + } +} +#endif \ No newline at end of file diff --git a/Svelto.ECS/Extensions/Unity/DOTS/NativeEGIDMultiMapper.cs b/Svelto.ECS/Extensions/Unity/DOTS/NativeEGIDMultiMapper.cs new file mode 100644 index 0000000..da67a17 --- /dev/null +++ b/Svelto.ECS/Extensions/Unity/DOTS/NativeEGIDMultiMapper.cs @@ -0,0 +1,43 @@ +#if UNITY_BURST +using System; +using Svelto.DataStructures; + +namespace Svelto.ECS +{ + public struct NativeEGIDMultiMapper:IDisposable where T : unmanaged, IEntityComponent + { + SveltoDictionary>, NativeStrategy>, + NativeStrategy>, NativeStrategy< + SveltoDictionary>, NativeStrategy>>> _dic; + + public NativeEGIDMultiMapper + (SveltoDictionary>, NativeStrategy>, + NativeStrategy>, NativeStrategy< + SveltoDictionary>, NativeStrategy>>> dictionary) + { + _dic = dictionary; + } + + public int count => (int) _dic.count; + + public void Dispose() + { + _dic.Dispose(); + } + + public ref T Entity(EGID entity) + { + ref var sveltoDictionary = ref _dic.GetValueByRef(entity.groupID); + return ref sveltoDictionary.GetValueByRef(entity.entityID); + } + + public bool Exists(EGID entity) + { + return _dic.TryFindIndex(entity.groupID, out var index) + && _dic.GetDirectValueByRef(index).ContainsKey(entity.entityID); + } + } +} +#endif \ No newline at end of file diff --git a/Svelto.ECS/Extensions/Unity/DOTS/NativeEntityComponentInitializer.cs b/Svelto.ECS/Extensions/Unity/DOTS/NativeEntityComponentInitializer.cs new file mode 100644 index 0000000..1d988b6 --- /dev/null +++ b/Svelto.ECS/Extensions/Unity/DOTS/NativeEntityComponentInitializer.cs @@ -0,0 +1,26 @@ +using Svelto.ECS.DataStructures; + +namespace Svelto.ECS +{ + public readonly ref struct NativeEntityComponentInitializer + { + readonly NativeBag _unsafeBuffer; + readonly UnsafeArrayIndex _index; + + public NativeEntityComponentInitializer(in NativeBag unsafeBuffer, UnsafeArrayIndex index) + { + _unsafeBuffer = unsafeBuffer; + _index = index; + } + + public void Init(in T component) where T : unmanaged, IEntityComponent + { + uint id = EntityComponentID.ID.Data; + + _unsafeBuffer.AccessReserved(_index)++; + + _unsafeBuffer.Enqueue(id); + _unsafeBuffer.Enqueue(component); + } + } +} \ No newline at end of file diff --git a/Svelto.ECS/Extensions/Unity/DOTS/NativeEntityFactory.cs b/Svelto.ECS/Extensions/Unity/DOTS/NativeEntityFactory.cs new file mode 100644 index 0000000..9888883 --- /dev/null +++ b/Svelto.ECS/Extensions/Unity/DOTS/NativeEntityFactory.cs @@ -0,0 +1,42 @@ +#if UNITY_BURST +using Svelto.ECS.DataStructures; +using Svelto.ECS.DataStructures.Unity; + +namespace Svelto.ECS +{ + public readonly struct NativeEntityFactory + { + readonly AtomicNativeBags _addOperationQueue; + readonly uint _index; + + internal NativeEntityFactory(AtomicNativeBags addOperationQueue, uint index) + { + _index = index; + _addOperationQueue = addOperationQueue; + } + + public NativeEntityComponentInitializer BuildEntity + (uint eindex, ExclusiveGroupStruct buildGroup, int threadIndex) + { + NativeBag unsafeBuffer = _addOperationQueue.GetBuffer(threadIndex + 1); + + unsafeBuffer.Enqueue(_index); + unsafeBuffer.Enqueue(new EGID(eindex, buildGroup)); + unsafeBuffer.ReserveEnqueue(out var index) = 0; + + return new NativeEntityComponentInitializer(unsafeBuffer, index); + } + + public NativeEntityComponentInitializer BuildEntity(EGID egid, int threadIndex) + { + NativeBag unsafeBuffer = _addOperationQueue.GetBuffer(threadIndex + 1); + + unsafeBuffer.Enqueue(_index); + unsafeBuffer.Enqueue(new EGID(egid.entityID, egid.groupID)); + unsafeBuffer.ReserveEnqueue(out var index) = 0; + + return new NativeEntityComponentInitializer(unsafeBuffer, index); + } + } +} +#endif \ No newline at end of file diff --git a/Svelto.ECS/Extensions/Unity/DOTS/NativeEntityRemove.cs b/Svelto.ECS/Extensions/Unity/DOTS/NativeEntityRemove.cs new file mode 100644 index 0000000..345a2f0 --- /dev/null +++ b/Svelto.ECS/Extensions/Unity/DOTS/NativeEntityRemove.cs @@ -0,0 +1,26 @@ +#if UNITY_BURST +using Svelto.ECS.DataStructures.Unity; + +namespace Svelto.ECS +{ + public readonly struct NativeEntityRemove + { + readonly AtomicNativeBags _removeQueue; + readonly uint _indexRemove; + + internal NativeEntityRemove(AtomicNativeBags EGIDsToRemove, uint indexRemove) + { + _removeQueue = EGIDsToRemove; + _indexRemove = indexRemove; + } + + public void RemoveEntity(EGID egid, int threadIndex) + { + var simpleNativeBag = _removeQueue.GetBuffer(threadIndex); + + simpleNativeBag.Enqueue(_indexRemove); + simpleNativeBag.Enqueue(egid); + } + } +} +#endif \ No newline at end of file diff --git a/Svelto.ECS/Extensions/Unity/DOTS/NativeEntitySwap.cs b/Svelto.ECS/Extensions/Unity/DOTS/NativeEntitySwap.cs new file mode 100644 index 0000000..52633a9 --- /dev/null +++ b/Svelto.ECS/Extensions/Unity/DOTS/NativeEntitySwap.cs @@ -0,0 +1,33 @@ +#if UNITY_BURST +using Svelto.ECS.DataStructures.Unity; + +namespace Svelto.ECS +{ + public readonly struct NativeEntitySwap + { + readonly AtomicNativeBags _swapQueue; + readonly uint _indexSwap; + + internal NativeEntitySwap(AtomicNativeBags EGIDsToSwap, uint indexSwap) + { + _swapQueue = EGIDsToSwap; + _indexSwap = indexSwap; + } + + public void SwapEntity(EGID from, EGID to, int threadIndex) + { + var simpleNativeBag = _swapQueue.GetBuffer(threadIndex); + simpleNativeBag.Enqueue(_indexSwap); + simpleNativeBag.Enqueue(new DoubleEGID(from, to)); + + } + + public void SwapEntity(EGID from, ExclusiveGroupStruct to, int threadIndex) + { + var simpleNativeBag = _swapQueue.GetBuffer(threadIndex); + simpleNativeBag.Enqueue(_indexSwap); + simpleNativeBag.Enqueue(new DoubleEGID(from, new EGID(from.entityID, to))); + } + } +} +#endif \ No newline at end of file diff --git a/Svelto.ECS/Extensions/Unity/DOTS/PureUECSSystemsGroup.cs b/Svelto.ECS/Extensions/Unity/DOTS/PureUECSSystemsGroup.cs index 64c50b0..889c9f6 100644 --- a/Svelto.ECS/Extensions/Unity/DOTS/PureUECSSystemsGroup.cs +++ b/Svelto.ECS/Extensions/Unity/DOTS/PureUECSSystemsGroup.cs @@ -21,6 +21,8 @@ namespace Svelto.ECS.Extensions.Unity return _jobHandle; } + public string name => nameof(PureUECSSystemsGroup); + readonly World _world; } } diff --git a/Svelto.ECS/Extensions/Unity/DOTS/SortedJobifedEnginesGroup.cs b/Svelto.ECS/Extensions/Unity/DOTS/SortedJobifedEnginesGroup.cs index 4648862..c51e5e2 100644 --- a/Svelto.ECS/Extensions/Unity/DOTS/SortedJobifedEnginesGroup.cs +++ b/Svelto.ECS/Extensions/Unity/DOTS/SortedJobifedEnginesGroup.cs @@ -5,26 +5,70 @@ using Svelto.Common; namespace Svelto.ECS.Extensions.Unity { - public abstract class SortedJobifedEnginesGroup + /// + /// Note sorted jobs run in serial + /// + /// + /// + public abstract class SortedJobifedEnginesGroup : IJobifiedGroupEngine where SequenceOrder : struct, ISequenceOrder where Interface : class, IJobifiedEngine { - protected SortedJobifedEnginesGroup(FasterReadOnlyList engines) + protected SortedJobifedEnginesGroup(FasterList engines) { + _name = "SortedJobifedEnginesGroup - "+this.GetType().Name; _instancedSequence = new Sequence(engines); } - public JobHandle Execute(JobHandle combinedHandles) + public JobHandle Execute(JobHandle inputHandles) { - var fasterReadOnlyList = _instancedSequence.items; - for (var index = 0; index < fasterReadOnlyList.Count; index++) + var sequenceItems = _instancedSequence.items; + JobHandle combinedHandles = inputHandles; + using (var profiler = new PlatformProfiler(_name)) { - var engine = fasterReadOnlyList[index]; - combinedHandles = JobHandle.CombineDependencies(combinedHandles, engine.Execute(combinedHandles)); + for (var index = 0; index < sequenceItems.count; index++) + { + var engine = sequenceItems[index]; + using (profiler.Sample(engine.name)) combinedHandles = JobHandle.CombineDependencies(combinedHandles, engine.Execute(combinedHandles)); + } + } + + return combinedHandles; + } + + public string name => _name; + + readonly string _name; + readonly Sequence _instancedSequence; + } + + public abstract class SortedJobifedEnginesGroup: IJobifiedGroupEngine + where SequenceOrder : struct, ISequenceOrder where Interface : class, IJobifiedEngine + { + protected SortedJobifedEnginesGroup(FasterList engines) + { + _name = "SortedJobifedEnginesGroup - "+this.GetType().Name; + _instancedSequence = new Sequence(engines); + } + + public JobHandle Execute(JobHandle combinedHandles, ref Parameter param) + { + var sequenceItems = _instancedSequence.items; + using (var profiler = new PlatformProfiler(_name)) + { + for (var index = 0; index < sequenceItems.count; index++) + { + var engine = sequenceItems[index]; + using (profiler.Sample(engine.name)) combinedHandles = + JobHandle.CombineDependencies(combinedHandles, engine.Execute(combinedHandles, ref param)); + } } return combinedHandles; } + public string name => _name; + + readonly string _name; readonly Sequence _instancedSequence; } } diff --git a/Svelto.ECS/Extensions/Unity/DOTS/SyncSveltoToUECSGroup.cs b/Svelto.ECS/Extensions/Unity/DOTS/SyncSveltoToUECSGroup.cs new file mode 100644 index 0000000..10e2eba --- /dev/null +++ b/Svelto.ECS/Extensions/Unity/DOTS/SyncSveltoToUECSGroup.cs @@ -0,0 +1,39 @@ +#if UNITY_ECS +using Svelto.Common; +using Unity.Entities; +using Unity.Jobs; + +namespace Svelto.ECS.Extensions.Unity +{ + [Sequenced(nameof(JobifiedSveltoEngines.CopySveltoToUECSEnginesGroup))] + [DisableAutoCreation] + public class SyncSveltoToUECSGroup : ComponentSystemGroup, IJobifiedEngine + { + public JobHandle Execute(JobHandle _jobHandle) + { + foreach (var engine in Systems) + (engine as SyncSveltoToUECSEngine).externalHandle = _jobHandle; + + Update(); + + return _jobHandle; + } + + public string name => nameof(SyncSveltoToUECSGroup); + + readonly SimulationSystemGroup _simulationSystemGroup; + } + + public abstract class SyncSveltoToUECSEngine : SystemBase, IEngine + { + internal JobHandle externalHandle; + protected abstract void Execute(); + + protected sealed override void OnUpdate() + { + Dependency = JobHandle.CombineDependencies(Dependency, externalHandle); + Execute(); + } + } +} +#endif \ No newline at end of file diff --git a/Svelto.ECS/Extensions/Unity/DOTS/UnityEntityDBExtensions.cs b/Svelto.ECS/Extensions/Unity/DOTS/UnityEntityDBExtensions.cs index d081e5b..feeba5d 100644 --- a/Svelto.ECS/Extensions/Unity/DOTS/UnityEntityDBExtensions.cs +++ b/Svelto.ECS/Extensions/Unity/DOTS/UnityEntityDBExtensions.cs @@ -1,12 +1,45 @@ -#if UNITY_2019_2_OR_NEWER +#if UNITY_JOBS using System; +using System.Runtime.CompilerServices; using Svelto.Common; +using Svelto.DataStructures; +using Svelto.ECS.Extensions.Unity; +using Svelto.ECS.Internal; using Unity.Jobs; -namespace Svelto.ECS.Extensions.Unity +namespace Svelto.ECS { + public static class UnityEntityDBExtensions2 + { + public static JobHandle ScheduleParallel + (this JOB job, uint iterations, JobHandle inputDeps) where JOB: struct, IJobParallelForBatch + { + if (iterations == 0) + return inputDeps; + var innerloopBatchCount = ProcessorCount.BatchSize(iterations); + return job.ScheduleBatch((int)iterations, innerloopBatchCount, inputDeps); + } + + public static JobHandle ScheduleParallel + (this JOB job, int iterations, JobHandle inputDeps) where JOB: struct, IJobParallelForBatch + { + if (iterations <= 0) + return inputDeps; + var innerloopBatchCount = ProcessorCount.BatchSize((uint) iterations); + return job.ScheduleBatch((int)iterations, innerloopBatchCount, inputDeps); + } + } + public static class UnityEntityDBExtensions { + internal static NativeEGIDMapper ToNativeEGIDMapper(this TypeSafeDictionary dic, + ExclusiveGroupStruct groupStructId) where T : unmanaged, IEntityComponent + { + var mapper = new NativeEGIDMapper(groupStructId, dic.implUnmgd); + + return mapper; + } + public static JobHandle ScheduleDispose (this T1 disposable, JobHandle inputDeps) where T1 : struct, IDisposable { @@ -23,9 +56,64 @@ namespace Svelto.ECS.Extensions.Unity public static JobHandle ScheduleParallel (this JOB job, uint iterations, JobHandle inputDeps) where JOB: struct, IJobParallelFor { + if (iterations == 0) + return inputDeps; var innerloopBatchCount = ProcessorCount.BatchSize(iterations); return job.Schedule((int)iterations, innerloopBatchCount, inputDeps); } + + public static JobHandle ScheduleParallel + (this JOB job, int iterations, JobHandle inputDeps) where JOB: struct, IJobParallelFor + { + if (iterations <= 0) + return inputDeps; + var innerloopBatchCount = ProcessorCount.BatchSize((uint) iterations); + return job.Schedule((int)iterations, innerloopBatchCount, inputDeps); + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static NativeEGIDMapper QueryNativeMappedEntities(this EntitiesDB entitiesDb, ExclusiveGroupStruct groupStructId) + where T : unmanaged, IEntityComponent + { + if (entitiesDb.SafeQueryEntityDictionary(groupStructId, out var typeSafeDictionary) == false) + throw new EntityGroupNotFoundException(typeof(T)); + + return (typeSafeDictionary as TypeSafeDictionary).ToNativeEGIDMapper(groupStructId); + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static bool TryQueryNativeMappedEntities(this EntitiesDB entitiesDb, ExclusiveGroupStruct groupStructId, + out NativeEGIDMapper mapper) + where T : unmanaged, IEntityComponent + { + mapper = default; + if (entitiesDb.SafeQueryEntityDictionary(groupStructId, out var typeSafeDictionary) == false || + typeSafeDictionary.count == 0) + return false; + + mapper = (typeSafeDictionary as TypeSafeDictionary).ToNativeEGIDMapper(groupStructId); + + return true; + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static NativeEGIDMultiMapper QueryNativeMappedEntities(this EntitiesDB entitiesDb, LocalFasterReadOnlyList groups) + where T : unmanaged, IEntityComponent + { + var dictionary = + new SveltoDictionary>, NativeStrategy>, + NativeStrategy>, NativeStrategy>, NativeStrategy>>> + (groups.count, Allocator.TempJob); + + foreach (var group in groups) + { + if (entitiesDb.SafeQueryEntityDictionary(group, out var typeSafeDictionary) == true) + if (typeSafeDictionary.count > 0) + dictionary.Add(group, ((TypeSafeDictionary)typeSafeDictionary).implUnmgd); + } + + return new NativeEGIDMultiMapper(dictionary); + } } } #endif \ No newline at end of file diff --git a/Svelto.ECS/Extensions/Unity/EGIDHolderImplementor.cs b/Svelto.ECS/Extensions/Unity/EGIDHolderImplementor.cs new file mode 100644 index 0000000..c4c4b88 --- /dev/null +++ b/Svelto.ECS/Extensions/Unity/EGIDHolderImplementor.cs @@ -0,0 +1,32 @@ +#if UNITY_5 || UNITY_5_3_OR_NEWER +using Svelto.ECS.Hybrid; +using UnityEngine; + +namespace Svelto.ECS.Extensions.Unity +{ + public interface IEGIDHolder + { + EGID ID { set; } + } + + public struct EGIDTrackerViewComponent : IEntityViewComponent + { +#pragma warning disable 649 + public IEGIDHolder holder; +#pragma warning restore 649 + + EGID _ID; + + public EGID ID + { + get => _ID; + set => _ID = holder.ID = value; + } + } + + public class EGIDHolderImplementor : MonoBehaviour, IEGIDHolder, IImplementor + { + public EGID ID { get; set; } + } +} +#endif \ No newline at end of file diff --git a/Svelto.ECS/Extensions/Unity/SveltoGUIHelper.cs b/Svelto.ECS/Extensions/Unity/SveltoGUIHelper.cs index bac02ff..5ed7d42 100644 --- a/Svelto.ECS/Extensions/Unity/SveltoGUIHelper.cs +++ b/Svelto.ECS/Extensions/Unity/SveltoGUIHelper.cs @@ -4,20 +4,47 @@ using UnityEngine; namespace Svelto.ECS.Extensions.Unity { + public static class EntityDescriptorHolderHelper + { + public static EntityComponentInitializer CreateEntity(this Transform contextHolder, EGID ID, + IEntityFactory factory, out T holder) + where T : MonoBehaviour, IEntityDescriptorHolder + { + holder = contextHolder.GetComponentInChildren(true); + var implementors = holder.GetComponents(); + + return factory.BuildEntity(ID, holder.GetDescriptor(), implementors); + } + + public static EntityComponentInitializer Create(this Transform contextHolder, EGID ID, + IEntityFactory factory) + where T : MonoBehaviour, IEntityDescriptorHolder + { + var holder = contextHolder.GetComponentInChildren(true); + var implementors = holder.GetComponents(); + + return factory.BuildEntity(ID, holder.GetDescriptor(), implementors); + } + } + public static class SveltoGUIHelper { public static T CreateFromPrefab(ref uint startIndex, Transform contextHolder, IEntityFactory factory, - ExclusiveGroup group, string groupNamePostfix = null) where T : MonoBehaviour, IEntityDescriptorHolder + ExclusiveGroup group, bool searchImplementorsInChildren = false, string groupNamePostfix = null) where T : MonoBehaviour, IEntityDescriptorHolder { - var holder = Create(new EGID(startIndex++, group), contextHolder, factory); - var childs = contextHolder.GetComponentsInChildren(true); + Create(new EGID(startIndex++, group), contextHolder, factory, out var holder); + var children = contextHolder.GetComponentsInChildren(true); - foreach (var child in childs) + foreach (var child in children) { + IImplementor[] childImplementors; if (child.GetType() != typeof(T)) { var monoBehaviour = child as MonoBehaviour; - var childImplementors = monoBehaviour.GetComponents(); + if (searchImplementorsInChildren == false) + childImplementors = monoBehaviour.GetComponents(); + else + childImplementors = monoBehaviour.GetComponentsInChildren(true); startIndex = InternalBuildAll( startIndex, child, @@ -31,27 +58,22 @@ namespace Svelto.ECS.Extensions.Unity return holder; } - - public static T Create(EGID ID, Transform contextHolder, IEntityFactory factory) + public static EntityComponentInitializer Create(EGID ID, Transform contextHolder, + IEntityFactory factory, out T holder, bool searchImplementorsInChildren = false) where T : MonoBehaviour, IEntityDescriptorHolder { - var holder = contextHolder.GetComponentInChildren(true); - DBC.ECS.Check.Assert(holder != null, $"`{nameof(holder)}` is null! No component of type " + - $"`{typeof(T)}` was found between its children."); - - var implementors = holder.GetComponents(); - - factory.BuildEntity(ID, holder.GetDescriptor(), implementors); + holder = contextHolder.GetComponentInChildren(true); + var implementors = searchImplementorsInChildren == false ? holder.GetComponents() : holder.GetComponentsInChildren(true) ; - return holder; + return factory.BuildEntity(ID, holder.GetDescriptor(), implementors); } - - public static EntityComponentInitializer CreateWithEntity(EGID ID, Transform contextHolder, - IEntityFactory factory, out T holder) + + public static EntityComponentInitializer Create(EGID ID, Transform contextHolder, + IEntityFactory factory, bool searchImplementorsInChildren = false) where T : MonoBehaviour, IEntityDescriptorHolder { - holder = contextHolder.GetComponentInChildren(true); - var implementors = holder.GetComponents(); + var holder = contextHolder.GetComponentInChildren(true); + var implementors = searchImplementorsInChildren == false ? holder.GetComponents() : holder.GetComponentsInChildren(true) ; return factory.BuildEntity(ID, holder.GetDescriptor(), implementors); } @@ -97,8 +119,8 @@ namespace Svelto.ECS.Extensions.Unity return startIndex; } - /// - /// Works like CreateAll but only builds entities with holders that have the same group specfied + /// + /// Works like CreateAll but only builds entities with holders that have the same group specified /// /// /// The group to match diff --git a/Svelto.ECS/Extensions/Unity/UnityEntitySubmissionScheduler.cs b/Svelto.ECS/Extensions/Unity/UnityEntitySubmissionScheduler.cs index c15f997..1f40879 100644 --- a/Svelto.ECS/Extensions/Unity/UnityEntitySubmissionScheduler.cs +++ b/Svelto.ECS/Extensions/Unity/UnityEntitySubmissionScheduler.cs @@ -5,7 +5,7 @@ using UnityEngine; namespace Svelto.ECS.Schedulers.Unity { - //The EntitySubmissionScheduler has been introduced to make the entity views submission logic platform independent + //The EntitySubmissionScheduler has been introduced to make the entity components submission logic platform independent //You can customize the scheduler if you wish public class UnityEntitiesSubmissionScheduler : IEntitiesSubmissionScheduler { @@ -27,17 +27,22 @@ namespace Svelto.ECS.Schedulers.Unity { yield return _wait; - onTick.Invoke(); + onTick(); } } readonly WaitForEndOfFrame _wait = new WaitForEndOfFrame(); readonly IEnumerator _coroutine; - public EnginesRoot.EntitiesSubmitter onTick; + public System.Action onTick; + } + + public UnityEntitiesSubmissionScheduler(string name = "ECSScheduler") + { + _scheduler = new GameObject(name).AddComponent(); + GameObject.DontDestroyOnLoad(_scheduler.gameObject); + _scheduler.onTick = SubmitEntities; } - - public UnityEntitiesSubmissionScheduler(string name = "ECSScheduler") { _name = name; } public void Dispose() { @@ -46,23 +51,22 @@ namespace Svelto.ECS.Schedulers.Unity Object.Destroy(_scheduler.gameObject); } } + + void SubmitEntities() + { + if (paused == false) + _onTick.Invoke(); + } - public EnginesRoot.EntitiesSubmitter onTick + EnginesRoot.EntitiesSubmitter IEntitiesSubmissionScheduler.onTick { - set - { - if (_scheduler == null) - { - _scheduler = new GameObject(_name).AddComponent(); - GameObject.DontDestroyOnLoad(_scheduler.gameObject); - } - - _scheduler.onTick = value; - } + set => _onTick = value; } + + public bool paused { get; set; } - Scheduler _scheduler; - readonly string _name; + readonly Scheduler _scheduler; + EnginesRoot.EntitiesSubmitter _onTick; } } #endif \ No newline at end of file diff --git a/Svelto.ECS/GenericEntityDescriptor.cs b/Svelto.ECS/GenericEntityDescriptor.cs index 6d95ec3..a10a149 100644 --- a/Svelto.ECS/GenericEntityDescriptor.cs +++ b/Svelto.ECS/GenericEntityDescriptor.cs @@ -2,33 +2,33 @@ { public abstract class GenericEntityDescriptor : IEntityDescriptor where T : struct, IEntityComponent { - static readonly IComponentBuilder[] _entityBuilders; - static GenericEntityDescriptor() { _entityBuilders = new IComponentBuilder[] {new ComponentBuilder()}; } + static readonly IComponentBuilder[] _componentBuilders; + static GenericEntityDescriptor() { _componentBuilders = new IComponentBuilder[] {new ComponentBuilder()}; } - public IComponentBuilder[] componentsToBuild => _entityBuilders; + public IComponentBuilder[] componentsToBuild => _componentBuilders; } public abstract class GenericEntityDescriptor : IEntityDescriptor where T : struct, IEntityComponent where U : struct, IEntityComponent { - static readonly IComponentBuilder[] _entityBuilders; + static readonly IComponentBuilder[] _componentBuilders; static GenericEntityDescriptor() { - _entityBuilders = new IComponentBuilder[] {new ComponentBuilder(), new ComponentBuilder()}; + _componentBuilders = new IComponentBuilder[] {new ComponentBuilder(), new ComponentBuilder()}; } - public IComponentBuilder[] componentsToBuild => _entityBuilders; + public IComponentBuilder[] componentsToBuild => _componentBuilders; } public abstract class GenericEntityDescriptor : IEntityDescriptor where T : struct, IEntityComponent where U : struct, IEntityComponent where V : struct, IEntityComponent { - static readonly IComponentBuilder[] _entityBuilders; + static readonly IComponentBuilder[] _componentBuilders; static GenericEntityDescriptor() { - _entityBuilders = new IComponentBuilder[] + _componentBuilders = new IComponentBuilder[] { new ComponentBuilder(), new ComponentBuilder(), @@ -36,18 +36,18 @@ }; } - public IComponentBuilder[] componentsToBuild => _entityBuilders; + public IComponentBuilder[] componentsToBuild => _componentBuilders; } public abstract class GenericEntityDescriptor : IEntityDescriptor where T : struct, IEntityComponent where U : struct, IEntityComponent where V : struct, IEntityComponent where W : struct, IEntityComponent { - static readonly IComponentBuilder[] _entityBuilders; + static readonly IComponentBuilder[] _componentBuilders; static GenericEntityDescriptor() { - _entityBuilders = new IComponentBuilder[] + _componentBuilders = new IComponentBuilder[] { new ComponentBuilder(), new ComponentBuilder(), @@ -56,18 +56,18 @@ }; } - public IComponentBuilder[] componentsToBuild => _entityBuilders; + public IComponentBuilder[] componentsToBuild => _componentBuilders; } public abstract class GenericEntityDescriptor : IEntityDescriptor where T : struct, IEntityComponent where U : struct, IEntityComponent where V : struct, IEntityComponent where W : struct, IEntityComponent where X : struct, IEntityComponent { - static readonly IComponentBuilder[] _entityBuilders; + static readonly IComponentBuilder[] _componentBuilders; static GenericEntityDescriptor() { - _entityBuilders = new IComponentBuilder[] + _componentBuilders = new IComponentBuilder[] { new ComponentBuilder(), new ComponentBuilder(), @@ -77,18 +77,18 @@ }; } - public IComponentBuilder[] componentsToBuild => _entityBuilders; + public IComponentBuilder[] componentsToBuild => _componentBuilders; } public abstract class GenericEntityDescriptor : IEntityDescriptor where T : struct, IEntityComponent where U : struct, IEntityComponent where V : struct, IEntityComponent where W : struct, IEntityComponent where X : struct, IEntityComponent where Y : struct, IEntityComponent { - static readonly IComponentBuilder[] _entityBuilders; + static readonly IComponentBuilder[] _componentBuilders; static GenericEntityDescriptor() { - _entityBuilders = new IComponentBuilder[] + _componentBuilders = new IComponentBuilder[] { new ComponentBuilder(), new ComponentBuilder(), @@ -99,6 +99,6 @@ }; } - public IComponentBuilder[] componentsToBuild => _entityBuilders; + public IComponentBuilder[] componentsToBuild => _componentBuilders; } } \ No newline at end of file diff --git a/Svelto.ECS/GenericentityStreamConsumerFactory.cs b/Svelto.ECS/GenericentityStreamConsumerFactory.cs index 6aee53d..2e893c2 100644 --- a/Svelto.ECS/GenericentityStreamConsumerFactory.cs +++ b/Svelto.ECS/GenericentityStreamConsumerFactory.cs @@ -15,7 +15,7 @@ namespace Svelto.ECS return _enginesRoot.Target.GenerateConsumer(name, capacity); } - public Consumer GenerateConsumer(ExclusiveGroup group, string name, uint capacity) + public Consumer GenerateConsumer(ExclusiveGroupStruct @group, string name, uint capacity) where T : unmanaged, IEntityComponent { return _enginesRoot.Target.GenerateConsumer(group, name, capacity); @@ -30,7 +30,7 @@ namespace Svelto.ECS { Consumer GenerateConsumer(string name, uint capacity) where T : unmanaged, IEntityComponent; - Consumer GenerateConsumer(ExclusiveGroup group, string name, uint capacity) + Consumer GenerateConsumer(ExclusiveGroupStruct @group, string name, uint capacity) where T : unmanaged, IEntityComponent; } } \ No newline at end of file diff --git a/Svelto.ECS/GlobalTypeID.cs b/Svelto.ECS/GlobalTypeID.cs index 73b7bb5..70d1fb7 100644 --- a/Svelto.ECS/GlobalTypeID.cs +++ b/Svelto.ECS/GlobalTypeID.cs @@ -1,95 +1,53 @@ -#if UNITY_ECS -using System; -using System.Collections.Generic; -using System.Linq; -using System.Reflection; using System.Threading; +using Svelto.Common; using Svelto.DataStructures; using Svelto.ECS.DataStructures; -using Unity.Burst; namespace Svelto.ECS { public class GlobalTypeID { - internal static uint NextID() - { - return (uint) (Interlocked.Increment(ref value) - 1); - } + internal static uint NextID() { return (uint) (Interlocked.Increment(ref value) - 1); } - static GlobalTypeID() - { - value = 0; - } + static GlobalTypeID() { value = 0; } static int value; } - - static class EntityComponentID - { - internal static readonly SharedStatic ID = SharedStatic.GetOrCreate(); - } - - interface IFiller + + interface IFiller { void FillFromByteArray(EntityComponentInitializer init, NativeBag buffer); } - - static class UnmanagedTypeExtensions - { - private static Dictionary cachedTypes = - new Dictionary(); - - public static bool IsUnManaged() { return typeof(T).IsUnManaged(); } - - public static bool IsUnManaged(this Type t) - { - var result = false; - - if (cachedTypes.ContainsKey(t)) - return cachedTypes[t]; - else if (t.IsPrimitive || t.IsPointer || t.IsEnum) - result = true; - else if (t.IsGenericType || !t.IsValueType) - result = false; - else - result = t.GetFields(BindingFlags.Public | - BindingFlags.NonPublic | BindingFlags.Instance) - .All(x => x.FieldType.IsUnManaged()); - cachedTypes.Add(t, result); - return result; - } - } - - delegate void ForceUnmanagedCast(EntityComponentInitializer init, NativeBag buffer) where T : struct, IEntityComponent; - class Filler: IFiller where T : struct, IEntityComponent + class Filler : IFiller where T : struct, IEntityComponent { - static readonly ForceUnmanagedCast _action; - static Filler() { - var method = typeof(Trick).GetMethod(nameof(Trick.ForceUnmanaged)).MakeGenericMethod(typeof(T)); - _action = (ForceUnmanagedCast) Delegate.CreateDelegate(typeof(ForceUnmanagedCast), method); + DBC.ECS.Check.Require(UnmanagedTypeExtensions.IsUnmanaged() == true, "invalid type used"); } - + //it's an internal interface - void IFiller.FillFromByteArray(EntityComponentInitializer init, NativeBag buffer) + public void FillFromByteArray(EntityComponentInitializer init, NativeBag buffer) { - DBC.ECS.Check.Require(UnmanagedTypeExtensions.IsUnManaged() == true, "invalid type used"); + var component = buffer.Dequeue(); - _action(init, buffer); + init.Init(component); } - - static class Trick - { - public static void ForceUnmanaged(EntityComponentInitializer init, NativeBag buffer) where U : unmanaged, IEntityComponent - { - var component = buffer.Dequeue(); + } - init.Init(component); - } + static class EntityComponentID + { +#if UNITY_BURST + internal static readonly Unity.Burst.SharedStatic ID = + Unity.Burst.SharedStatic.GetOrCreate(); +#else + internal struct SharedStatic + { + public uint Data; } + + internal static SharedStatic ID; +#endif } static class EntityComponentIDMap @@ -101,11 +59,7 @@ namespace Svelto.ECS var location = EntityComponentID.ID.Data = GlobalTypeID.NextID(); TYPE_IDS.AddAt(location, entityBuilder); } - - internal static IFiller GetTypeFromID(uint typeId) - { - return TYPE_IDS[typeId]; - } + + internal static IFiller GetTypeFromID(uint typeId) { return TYPE_IDS[typeId]; } } -} -#endif \ No newline at end of file +} \ No newline at end of file diff --git a/Svelto.ECS/GroupCompound.cs b/Svelto.ECS/GroupCompound.cs index d232e96..6de2cf0 100644 --- a/Svelto.ECS/GroupCompound.cs +++ b/Svelto.ECS/GroupCompound.cs @@ -1,82 +1,95 @@ using System; -using Svelto.ECS.Internal; +using Svelto.DataStructures; namespace Svelto.ECS { - public static class GroupCompound + public abstract class GroupCompound where G1 : GroupTag where G2 : GroupTag where G3 : GroupTag { - public static readonly ExclusiveGroupStruct[] Groups; + static readonly FasterList _Groups; + + public static FasterReadOnlyList Groups => new FasterReadOnlyList(_Groups); static GroupCompound() { - if ((Groups = GroupCompound.Groups) == null) - if ((Groups = GroupCompound.Groups) == null) - if ((Groups = GroupCompound.Groups) == null) - if ((Groups = GroupCompound.Groups) == null) - if ((Groups = GroupCompound.Groups) == null) + if ((_Groups = GroupCompound._Groups) == null) + if ((_Groups = GroupCompound._Groups) == null) + if ((_Groups = GroupCompound._Groups) == null) + if ((_Groups = GroupCompound._Groups) == null) + if ((_Groups = GroupCompound._Groups) == null) { - Groups = new ExclusiveGroupStruct[1]; + _Groups = new FasterList(1); var Group = new ExclusiveGroup(); - Groups[0] = Group; - - Console.LogDebug("".FastConcat(typeof(G1).ToString().FastConcat("-", typeof(G2).ToString(), "-").FastConcat(typeof(G3).ToString(), "- Initialized ", Groups[0].ToString()), "")); + _Groups.Add(Group); GroupCompound.Add(Group); // and must share the same array GroupCompound.Add(Group); GroupCompound.Add(Group); - + + //This is done here to be sure that the group is added once per group tag + //(if done inside the previous group compound it would be added multiple times) GroupTag.Add(Group); GroupTag.Add(Group); GroupTag.Add(Group); + +#if DEBUG + GroupMap.idToName[(uint) Group] = $"Compound: {typeof(G1).Name}-{typeof(G2).Name}-{typeof(G3).Name}"; +#endif } - else - Console.LogDebug(typeof(G1).ToString().FastConcat("-", typeof(G2).ToString(), "-").FastConcat(typeof(G3).ToString(), "-", Groups[0].ToString())); + } + + public static void Add(ExclusiveGroupStruct @group) + { + for (int i = 0; i < _Groups.count; ++i) + if (_Groups[i] == group) + throw new Exception("temporary must be transformed in unit test"); + + _Groups.Add(group); + + // GroupCompound._Groups = _Groups; } - public static ExclusiveGroupStruct BuildGroup => new ExclusiveGroupStruct(Groups[0]); + public static ExclusiveGroupStruct BuildGroup => new ExclusiveGroupStruct(_Groups[0]); } - public static class GroupCompound where G1 : GroupTag where G2 : GroupTag + public abstract class GroupCompound where G1 : GroupTag where G2 : GroupTag { - public static ExclusiveGroupStruct[] Groups; + static FasterList _Groups; + public static FasterReadOnlyList Groups => new FasterReadOnlyList(_Groups); static GroupCompound() { - Groups = GroupCompound.Groups; + _Groups = GroupCompound._Groups; - if (Groups == null) + if (_Groups == null) { - Groups = new ExclusiveGroupStruct[1]; + _Groups = new FasterList(1); var Group = new ExclusiveGroup(); - Groups[0] = Group; + _Groups.Add(Group); - Console.LogDebug("".FastConcat(typeof(G1).ToString().FastConcat("-", typeof(G2).ToString(), "- initialized ", Groups[0].ToString()), "")); - //every abstract group preemptively adds this group, it may or may not be empty in future GroupTag.Add(Group); GroupTag.Add(Group); + +#if DEBUG + GroupMap.idToName[(uint) Group] = $"Compound: {typeof(G1).Name}-{typeof(G2).Name}"; +#endif } - else - Console.LogDebug(typeof(G1).ToString().FastConcat("-", typeof(G2).ToString(), "-", Groups[0].ToString())); } - public static ExclusiveGroupStruct BuildGroup => new ExclusiveGroupStruct(Groups[0]); + public static ExclusiveGroupStruct BuildGroup => new ExclusiveGroupStruct(_Groups[0]); public static void Add(ExclusiveGroupStruct @group) { - for (int i = 0; i < Groups.Length; ++i) - if (Groups[i] == group) + for (int i = 0; i < _Groups.count; ++i) + if (_Groups[i] == group) throw new Exception("temporary must be transformed in unit test"); - - Array.Resize(ref Groups, Groups.Length + 1); - - Groups[Groups.Length - 1] = group; - GroupCompound.Groups = Groups; + _Groups.Add(group); - Console.LogDebug(typeof(G1).ToString().FastConcat("-", typeof(G2).ToString(), "- Add ", group.ToString())); + //unit test this to check if it's necessary + // GroupCompound._Groups = _Groups; } } @@ -86,27 +99,28 @@ namespace Svelto.ECS //groups with the same adjective, a group tag needs to hold all the groups sharing it. public abstract class GroupTag where T : GroupTag { - public static ExclusiveGroupStruct[] Groups = new ExclusiveGroupStruct[1]; + static FasterList _Groups = new FasterList(1); + + public static FasterReadOnlyList Groups => new FasterReadOnlyList(_Groups); static GroupTag() { - Groups[0] = new ExclusiveGroup(); - - Console.LogDebug(typeof(T).ToString() + "-" + Groups[0].ToString()); + _Groups.Add(new ExclusiveGroup()); } //Each time a new combination of group tags is found a new group is added. - internal static void Add(ExclusiveGroup @group) + internal static void Add(ExclusiveGroupStruct @group) { - for (int i = 0; i < Groups.Length; ++i) - if (Groups[i] == group) + for (int i = 0; i < _Groups.count; ++i) + if (_Groups[i] == group) throw new Exception("temporary must be transformed in unit test"); - Array.Resize(ref Groups, Groups.Length + 1); - - Groups[Groups.Length - 1] = group; - - Console.LogDebug(typeof(T).ToString().FastConcat("- Add ", group.ToString())); + _Groups.Add(group); +#if DEBUG + GroupMap.idToName[(uint) group] = $"Compound: {typeof(T).Name}"; +#endif } + + public static ExclusiveGroupStruct BuildGroup => new ExclusiveGroupStruct(_Groups[0]); } } diff --git a/Svelto.ECS/IEntitiesDB.cs b/Svelto.ECS/IEntitiesDB.cs index 70cdb47..e1a2822 100644 --- a/Svelto.ECS/IEntitiesDB.cs +++ b/Svelto.ECS/IEntitiesDB.cs @@ -1,7 +1,9 @@ +using Svelto.DataStructures; + namespace Svelto.ECS { - public delegate void ExecuteOnAllEntitiesAction(T[] prefabStruct, ExclusiveGroupStruct group, + public delegate void ExecuteOnAllEntitiesAction(IBuffer prefabStruct, ExclusiveGroupStruct group, uint count, EntitiesDB db, ref W instances); - public delegate void ExecuteOnAllEntitiesAction(T[] entities, ExclusiveGroupStruct group, + public delegate void ExecuteOnAllEntitiesAction(IBuffer entities, ExclusiveGroupStruct group, uint count, EntitiesDB db); } diff --git a/Svelto.ECS/IEntityFactory.cs b/Svelto.ECS/IEntityFactory.cs index a281761..814babc 100644 --- a/Svelto.ECS/IEntityFactory.cs +++ b/Svelto.ECS/IEntityFactory.cs @@ -1,5 +1,5 @@ +using System; using System.Collections.Generic; -using Svelto.ECS.Internal; namespace Svelto.ECS { @@ -34,7 +34,7 @@ namespace Svelto.ECS /// itself in terms of EntityComponents to build. The Implementors are passed to fill the /// references of the EntityComponents components. Please read the articles on my blog /// to understand better the terminologies - /// Using this function is like building a normal entity, but the entity views + /// Using this function is like building a normal entity, but the entity components /// are grouped by groupID to be more efficiently processed inside engines and /// improve cache locality. Either class entityComponents and struct entityComponents can be /// grouped. @@ -65,8 +65,11 @@ namespace Svelto.ECS EntityComponentInitializer BuildEntity(EGID egid, T entityDescriptor, IEnumerable implementors = null) where T : IEntityDescriptor; -#if UNITY_ECS - NativeEntityFactory ToNative(Unity.Collections.Allocator allocator) where T : IEntityDescriptor, new(); + EntityComponentInitializer BuildEntity + (EGID egid, IComponentBuilder[] componentsToBuild, Type type, IEnumerable implementors = null); + +#if UNITY_BURST + NativeEntityFactory ToNative(string memberName) where T : IEntityDescriptor, new(); #endif } } \ No newline at end of file diff --git a/Svelto.ECS/IEntityFunctions.cs b/Svelto.ECS/IEntityFunctions.cs index 9e8b0e1..e42d410 100644 --- a/Svelto.ECS/IEntityFunctions.cs +++ b/Svelto.ECS/IEntityFunctions.cs @@ -27,9 +27,9 @@ namespace Svelto.ECS void SwapEntityGroup(EGID fromID, EGID toId, ExclusiveGroupStruct mustBeFromGroup) where T : IEntityDescriptor, new(); -#if UNITY_ECS - NativeEntityRemove ToNativeRemove() where T : IEntityDescriptor, new(); - NativeEntitySwap ToNativeSwap() where T : IEntityDescriptor, new(); +#if UNITY_BURST + NativeEntityRemove ToNativeRemove(string memberName) where T : IEntityDescriptor, new(); + NativeEntitySwap ToNativeSwap(string memberName) where T : IEntityDescriptor, new(); #endif } } \ No newline at end of file diff --git a/Svelto.ECS/IReactOnSwap.cs b/Svelto.ECS/IReactOnSwap.cs index 85210c1..e8e1e32 100644 --- a/Svelto.ECS/IReactOnSwap.cs +++ b/Svelto.ECS/IReactOnSwap.cs @@ -5,8 +5,5 @@ namespace Svelto.ECS public interface IReactOnSwap : IReactOnSwap where T : IEntityComponent { void MovedTo(ref T entityComponent, ExclusiveGroupStruct previousGroup, EGID egid); -#if SEEMS_UNNECESSARY - void MovedFrom(ref T entityComponent, EGID egid); -#endif } } \ No newline at end of file diff --git a/Svelto.ECS/NamedExclusiveGroup.cs b/Svelto.ECS/NamedExclusiveGroup.cs new file mode 100644 index 0000000..61dc15e --- /dev/null +++ b/Svelto.ECS/NamedExclusiveGroup.cs @@ -0,0 +1,23 @@ +namespace Svelto.ECS +{ + /// + /// still experimental alternative to ExclusiveGroup, use this like: + /// use this like: + /// public class TriggersGroup : ExclusiveGroup {} + /// + /// + public abstract class NamedExclusiveGroup + { + public static ExclusiveGroup Group = new ExclusiveGroup(); + public static string name = typeof(T).FullName; + + static NamedExclusiveGroup() + { +#if DEBUG + GroupMap.idToName[(uint) Group] = name; +#endif + } + // protected NamedExclusiveGroup(string recognizeAs) : base(recognizeAs) {} + // protected NamedExclusiveGroup(ushort range) : base(range) {} + } +} \ No newline at end of file diff --git a/Svelto.ECS/NativeEGIDMapper.cs b/Svelto.ECS/NativeEGIDMapper.cs deleted file mode 100644 index 6af2f56..0000000 --- a/Svelto.ECS/NativeEGIDMapper.cs +++ /dev/null @@ -1,79 +0,0 @@ -using System; -using System.Runtime.CompilerServices; -using Svelto.DataStructures; - -namespace Svelto.ECS -{ - public readonly struct NativeEGIDMapper:IDisposable where T : unmanaged, IEntityComponent - { - readonly NativeFasterDictionary map; - public ExclusiveGroupStruct groupID { get; } - - public NativeEGIDMapper(ExclusiveGroupStruct groupStructId, NativeFasterDictionary 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 NBGetArrayAndEntityIndex(uint entityID, out uint index) - { - if (map.TryFindIndex(entityID, out index)) - { - return new NB(map.unsafeValues, map.count, map.capacity); - } - - throw new ECSException("Entity not found"); - } - - public unsafe bool TryGetArrayAndEntityIndex(uint entityID, out uint index, out NB array) - { - if (map.TryFindIndex(entityID, out index)) - { - array = new NB(map.unsafeValues, map.count, map.capacity); - return true; - } - - array = default; - return false; - } - - public void Dispose() - { - map.Dispose(); - } - - public bool Exists(uint idEntityId) - { - return map.count > 0 && map.TryFindIndex(idEntityId, out _); - } - } -} \ No newline at end of file diff --git a/Svelto.ECS/QueryGroups.cs b/Svelto.ECS/QueryGroups.cs index 2a00660..a22f4b5 100644 --- a/Svelto.ECS/QueryGroups.cs +++ b/Svelto.ECS/QueryGroups.cs @@ -1,38 +1,77 @@ +using System.Threading; using Svelto.DataStructures; -using Svelto.ECS.Internal; -namespace Svelto.ECS +namespace Svelto.ECS.Experimental { - public struct QueryGroups + struct GroupsList { - public readonly FasterList groups; + static GroupsList() + { + groups = new FasterList(); + } + + static readonly FasterList groups; - public QueryGroups(FasterDictionary findGroups) + public FasterList reference => groups; + } + + public ref struct QueryGroups + { + static readonly ThreadLocal groups = new ThreadLocal(); + + public QueryGroups(LocalFasterReadOnlyList findGroups) { - var findGroupsCount = findGroups.count; - groups = new FasterList(findGroupsCount); - foreach (var keyvalue in findGroups) + var groupsValue = groups.Value; + var group = groupsValue.reference; + + group.FastClear(); + for (int i = 0; i < findGroups.count; i++) { - groups.Add(new ExclusiveGroupStruct(keyvalue.Key)); + group.Add(findGroups[i]); } } - public QueryGroups Except(ExclusiveGroupStruct[] groupsToIgnore) + public QueryResult Except(ExclusiveGroupStruct[] groupsToIgnore) { - var groupsCount = groups.count; + var group = groups.Value.reference; + var groupsCount = group.count; for (int i = 0; i < groupsToIgnore.Length; i++) { for (int j = 0; j < groupsCount; j++) - if (groupsToIgnore[i] == groups[j]) + if (groupsToIgnore[i] == group[j]) { - groups.UnorderedRemoveAt(j); + group.UnorderedRemoveAt(j); j--; groupsCount--; } } - return this; + return new QueryResult(group); } + + public QueryResult Except(ExclusiveGroupStruct groupsToIgnore) + { + var group = groups.Value.reference; + var groupsCount = group.count; + + for (int j = 0; j < groupsCount; j++) + if (groupsToIgnore == group[j]) + { + group.UnorderedRemoveAt(j); + j--; + groupsCount--; + } + + return new QueryResult(group); + } + } + + public readonly ref struct QueryResult + { + readonly FasterReadOnlyList _group; + public QueryResult(FasterList @group) { _group = @group; } + + public FasterReadOnlyList result => _group; } } \ No newline at end of file diff --git a/Svelto.ECS/Serialization/DefaultSerializer.cs b/Svelto.ECS/Serialization/DefaultSerializer.cs index 3c705ac..4474863 100644 --- a/Svelto.ECS/Serialization/DefaultSerializer.cs +++ b/Svelto.ECS/Serialization/DefaultSerializer.cs @@ -9,9 +9,12 @@ namespace Svelto.ECS.Serialization var _type = typeof(T); foreach (var field in _type.GetFields()) - if (field.FieldType.ContainsCustomAttribute(typeof(DoNotSerializeAttribute)) && + { + var fieldFieldType = field.FieldType; + if (fieldFieldType.ContainsCustomAttribute(typeof(DoNotSerializeAttribute)) && field.IsPrivate == false) - throw new ECSException("field cannot be serialised ".FastConcat(_type.FullName)); + throw new ECSException($"field cannot be serialised {fieldFieldType} in {_type.FullName}"); + } if (_type.GetProperties().Length > (ComponentBuilder.HAS_EGID ? 1 : 0)) throw new ECSException("serializable entity struct must be property less ".FastConcat(_type.FullName)); diff --git a/Svelto.ECS/Serialization/DefaultVersioningFactory.cs b/Svelto.ECS/Serialization/DefaultVersioningFactory.cs index 971aae0..2e3dfa4 100644 --- a/Svelto.ECS/Serialization/DefaultVersioningFactory.cs +++ b/Svelto.ECS/Serialization/DefaultVersioningFactory.cs @@ -1,33 +1,30 @@ using System.Collections.Generic; +using Svelto.Common; namespace Svelto.ECS.Serialization { public class DefaultVersioningFactory : IDeserializationFactory where T : IEntityDescriptor, new() { - readonly IEntityFactory _factory; readonly IEnumerable _implementors; - public DefaultVersioningFactory(IEntityFactory factory) - { - _factory = factory; - } + public DefaultVersioningFactory() {} - public DefaultVersioningFactory(IEntityFactory factory, IEnumerable implementors) + public DefaultVersioningFactory(IEnumerable implementors) { - _factory = factory; _implementors = implementors; } - public EntityComponentInitializer BuildDeserializedEntity(EGID egid, - ISerializationData serializationData, - ISerializableEntityDescriptor entityDescriptor, - int serializationType, - IEntitySerialization entitySerialization) + public EntityComponentInitializer BuildDeserializedEntity + (EGID egid, ISerializationData serializationData, ISerializableEntityDescriptor entityDescriptor + , int serializationType, IEntitySerialization entitySerialization, IEntityFactory factory + , bool enginesRootIsDeserializationOnly) { - var initializer = _factory.BuildEntity(egid, _implementors); + var entityDescriptorEntitiesToSerialize = enginesRootIsDeserializationOnly ? entityDescriptor.entitiesToSerialize : entityDescriptor.componentsToBuild; + + var initializer = factory.BuildEntity(egid, entityDescriptorEntitiesToSerialize, TypeCache.type, _implementors); - entitySerialization.DeserializeEntityComponents(serializationData, entityDescriptor, ref initializer, - serializationType); + entitySerialization.DeserializeEntityComponents(serializationData, entityDescriptor, ref initializer + , serializationType); return initializer; } diff --git a/Svelto.ECS/Serialization/EnginesRoot.GenericEntitySerialization.cs b/Svelto.ECS/Serialization/EnginesRoot.GenericEntitySerialization.cs index afdfc26..c333630 100644 --- a/Svelto.ECS/Serialization/EnginesRoot.GenericEntitySerialization.cs +++ b/Svelto.ECS/Serialization/EnginesRoot.GenericEntitySerialization.cs @@ -43,25 +43,25 @@ namespace Svelto.ECS uint descriptorHash = serializableEntityHeader.descriptorHash; SerializationDescriptorMap serializationDescriptorMap = _enginesRoot.serializationDescriptorMap; - var factory = serializationDescriptorMap.GetSerializationFactory(descriptorHash); + IDeserializationFactory factory = serializationDescriptorMap.GetSerializationFactory(descriptorHash); var entityDescriptor = serializationDescriptorMap.GetDescriptorFromHash(descriptorHash); - //default factory - //todo: we have a default factory, why don't we always register that instead? - if (factory == null) - { - var initializer = _enginesRoot.BuildEntity(egid, - _enginesRoot._isDeserializationOnly ? entityDescriptor.entitiesToSerialize - : entityDescriptor.componentsToBuild); - - DeserializeEntityComponents(serializationData, entityDescriptor, ref initializer, serializationType); - - return initializer; - } + // //default factory + // //todo: we have a default factory, why don't we always register that instead? + // if (factory == null) + // { + // var initializer = _enginesRoot.BuildEntity(egid, + // _enginesRoot._isDeserializationOnly ? entityDescriptor.entitiesToSerialize + // : entityDescriptor.componentsToBuild, entityDescriptor.realType); + // + // DeserializeEntityComponents(serializationData, entityDescriptor, ref initializer, serializationType); + // + // return initializer; + // } //custom factory return factory.BuildDeserializedEntity(egid, serializationData, entityDescriptor, serializationType, - this); + this, this._enginesRoot.GenerateEntityFactory(), _enginesRoot._isDeserializationOnly); } public void DeserializeEntity(ISerializationData serializationData, int serializationType) @@ -120,14 +120,11 @@ namespace Svelto.ECS uint descriptorHash = serializableEntityComponent.descriptorHash; var entityDescriptor = serializationDescriptorMap.GetDescriptorFromHash(descriptorHash); - var entitySubmitOperation = new EntitySubmitOperation( - EntitySubmitOperationType.Swap, - localEgid, - toEgid, - entityDescriptor.componentsToBuild); + var entitySubmitOperation = new EntitySubmitOperation(EntitySubmitOperationType.Swap, + localEgid, toEgid, entityDescriptor.componentsToBuild); - _enginesRoot.CheckRemoveEntityID(localEgid); - _enginesRoot.CheckAddEntityID(toEgid); + _enginesRoot.CheckRemoveEntityID(localEgid, entityDescriptor.realType); + _enginesRoot.CheckAddEntityID(toEgid, entityDescriptor.realType); _enginesRoot.QueueEntitySubmitOperation(entitySubmitOperation); } @@ -141,7 +138,7 @@ namespace Svelto.ECS SerializationDescriptorMap serializationDescriptorMap = _enginesRoot.serializationDescriptorMap; var entityDescriptor = serializationDescriptorMap.GetDescriptorFromHash(descriptorHash); - _enginesRoot.CheckRemoveEntityID(egid); + _enginesRoot.CheckRemoveEntityID(egid, entityDescriptor.realType); var entitySubmitOperation = new EntitySubmitOperation( EntitySubmitOperationType.Remove, diff --git a/Svelto.ECS/Serialization/EntitiesDB.DescriptorMap.cs b/Svelto.ECS/Serialization/EntitiesDB.DescriptorMap.cs index 2d16d84..0cb062b 100644 --- a/Svelto.ECS/Serialization/EntitiesDB.DescriptorMap.cs +++ b/Svelto.ECS/Serialization/EntitiesDB.DescriptorMap.cs @@ -26,6 +26,7 @@ namespace Svelto.ECS Assembly[] assemblies = AppDomain.CurrentDomain.GetAssemblies(); // Assembly executingAssembly = Assembly.GetExecutingAssembly(); + Type d1 = typeof(DefaultVersioningFactory<>); foreach (Assembly assembly in assemblies) { // if (assembly.GetReferencedAssemblies().Contains(executingAssembly.GetName())) @@ -38,7 +39,7 @@ namespace Svelto.ECS { var descriptor = Activator.CreateInstance(type) as ISerializableEntityDescriptor; - RegisterEntityDescriptor(descriptor); + RegisterEntityDescriptor(descriptor, type, d1); } } } @@ -60,7 +61,7 @@ namespace Svelto.ECS } } - void RegisterEntityDescriptor(ISerializableEntityDescriptor descriptor) + void RegisterEntityDescriptor(ISerializableEntityDescriptor descriptor, Type type, Type d1) { if (descriptor == null) { @@ -78,6 +79,10 @@ namespace Svelto.ECS #endif _descriptors[descriptorHash] = descriptor; + Type[] typeArgs = {type}; + var makeGenericType = d1.MakeGenericType(typeArgs); + var instance = Activator.CreateInstance(makeGenericType); + _factories.Add(descriptorHash, instance as IDeserializationFactory); } public ISerializableEntityDescriptor GetDescriptorFromHash(uint descriptorID) @@ -92,13 +97,13 @@ namespace Svelto.ECS public IDeserializationFactory GetSerializationFactory(uint descriptorID) { - return _factories.TryGetValue(descriptorID, out var factory) ? factory : null; + return _factories[descriptorID]; } public void RegisterSerializationFactory(IDeserializationFactory deserializationFactory) where Descriptor : ISerializableEntityDescriptor, new() { - _factories.Add(SerializationEntityDescriptorTemplate.hash, deserializationFactory); + _factories[SerializationEntityDescriptorTemplate.hash] = deserializationFactory; } diff --git a/Svelto.ECS/Serialization/HashNameAttribute.cs b/Svelto.ECS/Serialization/HashNameAttribute.cs index f2c6ed2..efead7e 100644 --- a/Svelto.ECS/Serialization/HashNameAttribute.cs +++ b/Svelto.ECS/Serialization/HashNameAttribute.cs @@ -10,6 +10,6 @@ namespace Svelto.ECS.Serialization _name = name; } - internal string _name; + internal readonly string _name; } } \ No newline at end of file diff --git a/Svelto.ECS/Serialization/IDeserializationFactory.cs b/Svelto.ECS/Serialization/IDeserializationFactory.cs index 9fce087..b35226e 100644 --- a/Svelto.ECS/Serialization/IDeserializationFactory.cs +++ b/Svelto.ECS/Serialization/IDeserializationFactory.cs @@ -2,8 +2,9 @@ namespace Svelto.ECS.Serialization { public interface IDeserializationFactory { - EntityComponentInitializer BuildDeserializedEntity(EGID egid, ISerializationData serializationData, - ISerializableEntityDescriptor entityDescriptor, int serializationType, - IEntitySerialization entitySerialization); + EntityComponentInitializer BuildDeserializedEntity + (EGID egid, ISerializationData serializationData, ISerializableEntityDescriptor entityDescriptor + , int serializationType, IEntitySerialization entitySerialization, IEntityFactory factory + , bool enginesRootIsDeserializationOnly); } } diff --git a/Svelto.ECS/Serialization/ISerializableEntityDescriptor.cs b/Svelto.ECS/Serialization/ISerializableEntityDescriptor.cs index bf61048..67726cd 100644 --- a/Svelto.ECS/Serialization/ISerializableEntityDescriptor.cs +++ b/Svelto.ECS/Serialization/ISerializableEntityDescriptor.cs @@ -1,8 +1,11 @@ +using System; + namespace Svelto.ECS.Serialization { public interface ISerializableEntityDescriptor : IEntityDescriptor { uint hash { get; } ISerializableComponentBuilder[] entitiesToSerialize { get; } + Type realType { get; } } } \ No newline at end of file diff --git a/Svelto.ECS/Serialization/PartialSerializer.cs b/Svelto.ECS/Serialization/PartialSerializer.cs index 443f363..42bc959 100644 --- a/Svelto.ECS/Serialization/PartialSerializer.cs +++ b/Svelto.ECS/Serialization/PartialSerializer.cs @@ -25,11 +25,13 @@ namespace Svelto.ECS.Serialization { if (myAttributes[j] is PartialSerializerFieldAttribute) { - if (myMembers[i].FieldType == typeof(EGID)) - throw new ECSException("EGID fields cannot be serialised ".FastConcat(myType.FullName)); + var fieldType = myMembers[i].FieldType; + if (fieldType.ContainsCustomAttribute(typeof(DoNotSerializeAttribute)) && + myMembers[i].IsPrivate == false) + throw new ECSException($"field cannot be serialised {fieldType} in {myType.FullName}"); var offset = Marshal.OffsetOf(myMembers[i].Name); - var sizeOf = (uint)Marshal.SizeOf(myMembers[i].FieldType); + var sizeOf = (uint)Marshal.SizeOf(fieldType); offsets.Add(((uint) offset.ToInt32(), sizeOf)); totalSize += sizeOf; } diff --git a/Svelto.ECS/Serialization/SerializableComponentBuilder.cs b/Svelto.ECS/Serialization/SerializableComponentBuilder.cs index de86568..d3ba176 100644 --- a/Svelto.ECS/Serialization/SerializableComponentBuilder.cs +++ b/Svelto.ECS/Serialization/SerializableComponentBuilder.cs @@ -14,7 +14,7 @@ namespace Svelto.ECS.Serialization public class SerializableComponentBuilder : ComponentBuilder, ISerializableComponentBuilder where T : unmanaged, IEntityComponent { - public static readonly uint SIZE = UnsafeUtils.SizeOf(); + public static readonly uint SIZE = (uint) MemoryUtilities.SizeOf(); public void Serialize (uint entityID, ITypeSafeDictionary dictionary, ISerializationData serializationData @@ -28,8 +28,7 @@ namespace Svelto.ECS.Serialization throw new ECSException("Entity Serialization failed"); } - var values = safeDictionary.unsafeValues; - ref T val = ref values[(int) index]; + ref T val = ref safeDictionary.GetDirectValueByRef(index); serializationData.dataPos = (uint) serializationData.data.count; @@ -50,8 +49,7 @@ namespace Svelto.ECS.Serialization throw new ECSException("Entity Deserialization failed"); } - var values = safeDictionary.unsafeValues; - ref T val = ref values[(int) index]; + ref T val = ref safeDictionary.GetDirectValueByRef(index); componentSerializer.DeserializeSafe(ref val, serializationData); } diff --git a/Svelto.ECS/Serialization/SerializableEntityDescriptor.cs b/Svelto.ECS/Serialization/SerializableEntityDescriptor.cs index b758bb6..d59d571 100644 --- a/Svelto.ECS/Serialization/SerializableEntityDescriptor.cs +++ b/Svelto.ECS/Serialization/SerializableEntityDescriptor.cs @@ -18,14 +18,14 @@ namespace Svelto.ECS.Serialization { IComponentBuilder[] defaultEntities = EntityDescriptorTemplate.descriptor.componentsToBuild; - var hashNameAttribute = _type.GetCustomAttribute(); + var hashNameAttribute = Type.GetCustomAttribute(); if (hashNameAttribute == null) { throw new Exception( - "HashName attribute not found on the serializable type ".FastConcat(_type.FullName)); + "HashName attribute not found on the serializable type ".FastConcat(Type.FullName)); } - _hash = DesignatedHash.Hash(Encoding.ASCII.GetBytes(hashNameAttribute._name)); + Hash = DesignatedHash.Hash(Encoding.ASCII.GetBytes(hashNameAttribute._name)); var (index, dynamicIndex) = SetupSpecialEntityComponent(defaultEntities, out ComponentsToBuild); if (index == -1) @@ -36,13 +36,13 @@ namespace Svelto.ECS.Serialization // Stores the hash of this EntityDescriptor ComponentsToBuild[index] = new ComponentBuilder(new SerializableEntityComponent { - descriptorHash = _hash + descriptorHash = Hash }); // If the current serializable is an ExtendibleDescriptor, I have to update it. if (dynamicIndex != -1) { - ComponentsToBuild[dynamicIndex] = new ComponentBuilder(new EntityInfoComponentView + ComponentsToBuild[dynamicIndex] = new ComponentBuilder(new EntityInfoViewComponent { componentsToBuild = ComponentsToBuild }); @@ -50,18 +50,18 @@ namespace Svelto.ECS.Serialization ///// var entitiesToSerialize = new FasterList(); - _entityComponentsToSerializeMap = new FasterDictionary, ISerializableComponentBuilder>(); + EntityComponentsToSerializeMap = new FasterDictionary, ISerializableComponentBuilder>(); foreach (IComponentBuilder e in defaultEntities) { if (e is ISerializableComponentBuilder serializableEntityBuilder) { var entityType = serializableEntityBuilder.GetEntityComponentType(); - _entityComponentsToSerializeMap[new RefWrapper(entityType)] = serializableEntityBuilder; + EntityComponentsToSerializeMap[new RefWrapper(entityType)] = serializableEntityBuilder; entitiesToSerialize.Add(serializableEntityBuilder); } } - _entitiesToSerialize = entitiesToSerialize.ToArray(); + EntitiesToSerialize = entitiesToSerialize.ToArray(); } static (int indexSerial, int indexDynamic) SetupSpecialEntityComponent @@ -75,13 +75,13 @@ namespace Svelto.ECS.Serialization for (var i = 0; i < length; ++i) { - if (defaultEntities[i].GetEntityComponentType() == _serializableStructType) + if (defaultEntities[i].GetEntityComponentType() == SerializableStructType) { indexSerial = i; --newLenght; } - if (defaultEntities[i].GetEntityComponentType() == EntityBuilderUtilities.ENTITY_STRUCT_INFO_VIEW) + if (defaultEntities[i].GetEntityComponentType() == ComponentBuilderUtilities.ENTITY_STRUCT_INFO_VIEW) { indexDynamic = i; } @@ -94,16 +94,17 @@ namespace Svelto.ECS.Serialization return (indexSerial, indexDynamic); } - public IComponentBuilder[] componentsToBuild => ComponentsToBuild; - public uint hash => _hash; - public ISerializableComponentBuilder[] entitiesToSerialize => _entitiesToSerialize; + public IComponentBuilder[] componentsToBuild => ComponentsToBuild; + public uint hash => Hash; + public Type realType => Type; + public ISerializableComponentBuilder[] entitiesToSerialize => EntitiesToSerialize; static readonly IComponentBuilder[] ComponentsToBuild; - static readonly FasterDictionary, ISerializableComponentBuilder> _entityComponentsToSerializeMap; - static readonly ISerializableComponentBuilder[] _entitiesToSerialize; + static readonly FasterDictionary, ISerializableComponentBuilder> EntityComponentsToSerializeMap; + static readonly ISerializableComponentBuilder[] EntitiesToSerialize; - static readonly uint _hash; - static readonly Type _serializableStructType = typeof(SerializableEntityComponent); - static readonly Type _type = typeof(TType); + static readonly uint Hash; + static readonly Type SerializableStructType = typeof(SerializableEntityComponent); + static readonly Type Type = typeof(TType); } } \ No newline at end of file diff --git a/Svelto.ECS/Serialization/SerializerExt.cs b/Svelto.ECS/Serialization/SerializerExt.cs index 4387d3b..45f705c 100644 --- a/Svelto.ECS/Serialization/SerializerExt.cs +++ b/Svelto.ECS/Serialization/SerializerExt.cs @@ -1,5 +1,3 @@ -using System; - namespace Svelto.ECS.Serialization { public static class SerializerExt { @@ -15,7 +13,7 @@ namespace Svelto.ECS.Serialization { // size == 0 is a special case when we don't know the size in advance if (componentSerializer.size != 0 && serializationData.dataPos != posBefore + componentSerializer.size) { - throw new IndexOutOfRangeException( + throw new System.IndexOutOfRangeException( $"Size mismatch when serializing {typeof(T).FullName} using {componentSerializer.GetType().FullName}, " + $"expected offset {posBefore + componentSerializer.size}, got {serializationData.dataPos}"); } @@ -34,7 +32,7 @@ namespace Svelto.ECS.Serialization { #if DEBUG && !PROFILE_SVELTO if (componentSerializer.size != 0 && serializationData.dataPos != posBefore + componentSerializer.size) { - throw new IndexOutOfRangeException( + throw new System.IndexOutOfRangeException( $"Size mismatch when deserializing {typeof(T).FullName} using {componentSerializer.GetType().FullName}, " + $"expected offset {posBefore + componentSerializer.size}, got {serializationData.dataPos}"); } diff --git a/Svelto.ECS/SetEGIDWithoutBoxing.cs b/Svelto.ECS/SetEGIDWithoutBoxing.cs index 6a3fa85..bc81645 100644 --- a/Svelto.ECS/SetEGIDWithoutBoxing.cs +++ b/Svelto.ECS/SetEGIDWithoutBoxing.cs @@ -1,4 +1,4 @@ -using System; +using System.Runtime.CompilerServices; namespace Svelto.ECS.Internal { @@ -14,16 +14,25 @@ namespace Svelto.ECS.Internal { if (ComponentBuilder.HAS_EGID) { +#if !ENABLE_IL2CPP var method = typeof(Trick).GetMethod(nameof(Trick.SetEGIDImpl)).MakeGenericMethod(typeof(T)); - return (SetEGIDWithoutBoxingActionCast) Delegate.CreateDelegate( + return (SetEGIDWithoutBoxingActionCast) System.Delegate.CreateDelegate( typeof(SetEGIDWithoutBoxingActionCast), method); +#else + return (ref T target, EGID egid) => + { + var needEgid = (target as INeedEGID); + needEgid.ID = egid; + target = (T) needEgid; + }; +#endif } return null; } - + static class Trick - { + { public static void SetEGIDImpl(ref U target, EGID egid) where U : struct, INeedEGID { target.ID = egid; diff --git a/Svelto.ECS/SimpleEntitiesSubmissionScheduler.cs b/Svelto.ECS/SimpleEntitiesSubmissionScheduler.cs index 3404b02..9d543c1 100644 --- a/Svelto.ECS/SimpleEntitiesSubmissionScheduler.cs +++ b/Svelto.ECS/SimpleEntitiesSubmissionScheduler.cs @@ -7,13 +7,16 @@ namespace Svelto.ECS { public void SubmitEntities() { - _onTick.Invoke(); + if (paused == false) + _onTick.Invoke(); } EnginesRoot.EntitiesSubmitter IEntitiesSubmissionScheduler.onTick { set => _onTick = value; } + + public bool paused { get; set; } public void Dispose() { } diff --git a/Svelto.ECS/Svelto.ECS.asmdef b/Svelto.ECS/Svelto.ECS.asmdef index ac3faf3..591e514 100644 --- a/Svelto.ECS/Svelto.ECS.asmdef +++ b/Svelto.ECS/Svelto.ECS.asmdef @@ -4,6 +4,7 @@ "Unity.Entities", "Unity.Collections", "Unity.Burst", + "Unity.Jobs", "Svelto.Common_3" ], "includePlatforms": [], @@ -20,6 +21,21 @@ "name": "com.unity.entities", "expression": "", "define": "UNITY_ECS" + }, + { + "name": "com.unity.burst", + "expression": "", + "define": "UNITY_BURST" + }, + { + "name": "com.unity.collections", + "expression": "", + "define": "UNITY_COLLECTIONS" + }, + { + "name": "com.unity.jobs", + "expression": "", + "define": "UNITY_JOBS" } ], "noEngineReferences": false diff --git a/Svelto.ECS/Svelto.ECS.csproj b/Svelto.ECS/Svelto.ECS.csproj index 9fdf31b..da96d32 100644 --- a/Svelto.ECS/Svelto.ECS.csproj +++ b/Svelto.ECS/Svelto.ECS.csproj @@ -15,7 +15,7 @@ true - + diff --git a/Svelto.ECS/TupleRef.cs b/Svelto.ECS/TupleRef.cs new file mode 100644 index 0000000..80ea9ee --- /dev/null +++ b/Svelto.ECS/TupleRef.cs @@ -0,0 +1,58 @@ +namespace Svelto.ECS +{ + public readonly ref struct TupleRef where T1 : struct, IEntityComponent + { + public readonly EntityCollections entities; + public readonly GroupsEnumerable groups; + + public TupleRef(in EntityCollections entityCollections, in GroupsEnumerable groupsEnumerable) + { + this.entities = entityCollections; + groups = groupsEnumerable; + } + } + + public readonly ref struct TupleRef where T1 : struct, IEntityComponent where T2 : struct, IEntityComponent + { + public readonly EntityCollections entities; + public readonly GroupsEnumerable groups; + + public TupleRef(in EntityCollections entityCollections, in GroupsEnumerable groupsEnumerable) + { + this.entities = entityCollections; + groups = groupsEnumerable; + } + } + + public readonly ref struct TupleRef where T1 : struct, IEntityComponent + where T2 : struct, IEntityComponent + where T3 : struct, IEntityComponent + { + public readonly EntityCollections entities; + public readonly GroupsEnumerable groups; + + public TupleRef + (in EntityCollections entityCollections, in GroupsEnumerable groupsEnumerable) + { + this.entities = entityCollections; + groups = groupsEnumerable; + } + } + + public readonly ref struct TupleRef where T1 : struct, IEntityComponent + where T2 : struct, IEntityComponent + where T3 : struct, IEntityComponent + where T4 : struct, IEntityComponent + { + public readonly EntityCollections entities; + public readonly GroupsEnumerable groups; + + public TupleRef + (in EntityCollections entityCollections + , in GroupsEnumerable groupsEnumerable) + { + this.entities = entityCollections; + groups = groupsEnumerable; + } + } +} \ No newline at end of file diff --git a/Svelto.ECS/TypeSafeDictionaryFactory.cs b/Svelto.ECS/TypeSafeDictionaryFactory.cs index 2b7f94f..7b2c550 100644 --- a/Svelto.ECS/TypeSafeDictionaryFactory.cs +++ b/Svelto.ECS/TypeSafeDictionaryFactory.cs @@ -6,9 +6,9 @@ namespace Svelto.ECS { public static ITypeSafeDictionary Create() { - return new TypeSafeDictionary(); + return new TypeSafeDictionary(1); } - + public static ITypeSafeDictionary Create(uint size) { return new TypeSafeDictionary(size); diff --git a/Svelto.ECS/package.json b/Svelto.ECS/package.json index f4717d5..120416d 100644 --- a/Svelto.ECS/package.json +++ b/Svelto.ECS/package.json @@ -3,13 +3,13 @@ "category": "Svelto", "description": "Svelto ECS C# Lightweight Data Oriented Entity Component System Framework", "dependencies": { - "com.sebaslab.svelto.common": "2.9.4" + "com.sebaslab.svelto.common": "3.0.0" }, "keywords": [ "svelto" ], "name": "com.sebaslab.svelto.ecs", "unity": "2019.2", - "version": "2.9.5", + "version": "3.0.0", "type": "library" }