diff --git a/src/Builtin/Runners.cs b/src/Builtin/BaseRunners.cs similarity index 89% rename from src/Builtin/Runners.cs rename to src/Builtin/BaseRunners.cs index 671f343..a618cb7 100644 --- a/src/Builtin/Runners.cs +++ b/src/Builtin/BaseRunners.cs @@ -22,12 +22,12 @@ public sealed class EcsPreInitRunner : EcsRunner, IEcsPreInitSystem { -#if DEBUG +#if DEBUG && !DISABLE_DRAGONECS_DEBUG private EcsProfilerMarker[] _markers; #endif public void PreInit(EcsPipeline pipeline) { -#if DEBUG +#if DEBUG && !DISABLE_DRAGONECS_DEBUG for (int i = 0; i < targets.Length; i++) { using (_markers[i].Auto()) @@ -38,7 +38,7 @@ #endif } -#if DEBUG +#if DEBUG && !DISABLE_DRAGONECS_DEBUG protected override void OnSetup() { _markers = new EcsProfilerMarker[targets.Length]; @@ -51,12 +51,12 @@ } public sealed class EcsInitRunner : EcsRunner, IEcsInitSystem { -#if DEBUG +#if DEBUG && !DISABLE_DRAGONECS_DEBUG private EcsProfilerMarker[] _markers; #endif public void Init(EcsPipeline pipeline) { -#if DEBUG +#if DEBUG && !DISABLE_DRAGONECS_DEBUG for (int i = 0; i < targets.Length; i++) { using (_markers[i].Auto()) @@ -67,7 +67,7 @@ #endif } -#if DEBUG +#if DEBUG && !DISABLE_DRAGONECS_DEBUG protected override void OnSetup() { _markers = new EcsProfilerMarker[targets.Length]; @@ -80,12 +80,12 @@ } public sealed class EcsRunRunner : EcsRunner, IEcsRunSystem { -#if DEBUG +#if DEBUG && !DISABLE_DRAGONECS_DEBUG private EcsProfilerMarker[] _markers; #endif public void Run(EcsPipeline pipeline) { -#if DEBUG +#if DEBUG && !DISABLE_DRAGONECS_DEBUG for (int i = 0; i < targets.Length; i++) { using (_markers[i].Auto()) @@ -97,7 +97,7 @@ #endif } -#if DEBUG +#if DEBUG && !DISABLE_DRAGONECS_DEBUG protected override void OnSetup() { _markers = new EcsProfilerMarker[targets.Length]; @@ -110,12 +110,12 @@ } public sealed class EcsDestroyRunner : EcsRunner, IEcsDestroySystem { -#if DEBUG +#if DEBUG && !DISABLE_DRAGONECS_DEBUG private EcsProfilerMarker[] _markers; #endif public void Destroy(EcsPipeline pipeline) { -#if DEBUG +#if DEBUG && !DISABLE_DRAGONECS_DEBUG for (int i = 0; i < targets.Length; i++) { using (_markers[i].Auto()) @@ -126,7 +126,7 @@ #endif } -#if DEBUG +#if DEBUG && !DISABLE_DRAGONECS_DEBUG protected override void OnSetup() { _markers = new EcsProfilerMarker[targets.Length]; diff --git a/src/Builtin/WorldRunners.cs b/src/Builtin/WorldRunners.cs new file mode 100644 index 0000000..5687b75 --- /dev/null +++ b/src/Builtin/WorldRunners.cs @@ -0,0 +1,87 @@ +namespace DCFApixels.DragonECS +{ + public interface IEcsComponentAdd : IEcsSystem + { + public void OnComponentAdd(int entityID); + } + public interface IEcsComponentWrite : IEcsSystem + { + public void OnComponentWrite(int entityID); + } + public interface IEcsComponentDel : IEcsSystem + { + public void OnComponentDel(int entityID); + } + public interface IEcsComponentLifecycle : IEcsComponentAdd, IEcsComponentWrite, IEcsComponentDel { } + public sealed class EcsEntityAddComponentRunner : EcsRunner, IEcsComponentAdd + { + public void OnComponentAdd(int entityID) + { + foreach (var item in targets) item.OnComponentAdd(entityID); + } + } + public sealed class EcsEntityChangeComponentRunner : EcsRunner, IEcsComponentWrite + { + public void OnComponentWrite(int entityID) + { + foreach (var item in targets) item.OnComponentWrite(entityID); + } + } + public sealed class EcsEntityDelComponentRunner : EcsRunner, IEcsComponentDel + { + public void OnComponentDel(int entityID) + { + foreach (var item in targets) item.OnComponentDel(entityID); + } + } + + + public interface IEcsEntityCreate : IEcsSystem + { + public void OnEntityCreate(ent entity); + } + public interface IEcsEntityDestroy : IEcsSystem + { + public void OnEntityDestroy(ent entity); + } + public interface IEcsEntityLifecycle : IEcsEntityCreate, IEcsEntityDestroy { } + public sealed class EcsEntityCreateRunner : EcsRunner, IEcsEntityCreate + { + public void OnEntityCreate(ent entity) + { + foreach (var item in targets) item.OnEntityCreate(entity); + } + } + public sealed class EcsEntityDestroyRunner : EcsRunner, IEcsEntityDestroy + { + public void OnEntityDestroy(ent entity) + { + foreach (var item in targets) item.OnEntityDestroy(entity); + } + } + + + public interface IEcsWorldCreate : IEcsSystem + { + public void OnWorldCreate(IEcsWorld world); + } + public interface IEcsWorldDestroy : IEcsSystem + { + public void OnWorldDestroy(IEcsWorld world); + } + public interface IEcsWorldLifecycle : IEcsWorldCreate, IEcsWorldDestroy { } + public sealed class EcsWorldCreateRunner : EcsRunner, IEcsWorldCreate + { + public void OnWorldCreate(IEcsWorld world) + { + foreach (var item in targets) item.OnWorldCreate(world); + } + } + public sealed class EcsWorldDestryRunner : EcsRunner, IEcsWorldDestroy + { + public void OnWorldDestroy(IEcsWorld world) + { + foreach (var item in targets) item.OnWorldDestroy(world); + } + } +} diff --git a/src/Builtin/Worlds.cs b/src/Builtin/Worlds.cs index 1d6a15d..64f2499 100644 --- a/src/Builtin/Worlds.cs +++ b/src/Builtin/Worlds.cs @@ -1,5 +1,11 @@ namespace DCFApixels.DragonECS { - public sealed class EcsDefaultWrold : EcsWorld { } - public sealed class EcsEventWrold : EcsWorld { } + public sealed class EcsDefaultWrold : EcsWorld + { + public EcsDefaultWrold(EcsPipeline pipeline) : base(pipeline) { } + } + public sealed class EcsEventWrold : EcsWorld + { + public EcsEventWrold(EcsPipeline pipeline) : base(pipeline) { } + } } diff --git a/src/Debug/EcsDebug.cs b/src/Debug/EcsDebug.cs index 0189032..21addfc 100644 --- a/src/Debug/EcsDebug.cs +++ b/src/Debug/EcsDebug.cs @@ -17,7 +17,7 @@ namespace DCFApixels.DragonECS [MethodImpl(MethodImplOptions.AggressiveInlining)] public AutoScope Auto() => new AutoScope(id); - public readonly struct AutoScope : IDisposable + public readonly ref struct AutoScope { private readonly int id; public AutoScope(int id) @@ -43,14 +43,14 @@ namespace DCFApixels.DragonECS [MethodImpl(MethodImplOptions.AggressiveInlining)] public static void Print(string tag, object v) { -#if !DISABLE_ECS_DEBUG +#if !DISABLE_DRAGONECS_DEBUG DebugService.Instance.Print(tag, v); #endif } [MethodImpl(MethodImplOptions.AggressiveInlining)] public static int RegisterMark(string name) { -#if !DISABLE_ECS_DEBUG +#if !DISABLE_DRAGONECS_DEBUG return DebugService.Instance.RegisterMark(name); #else return 0; @@ -59,21 +59,21 @@ namespace DCFApixels.DragonECS [MethodImpl(MethodImplOptions.AggressiveInlining)] public static void DeleteMark(string name) { -#if !DISABLE_ECS_DEBUG +#if !DISABLE_DRAGONECS_DEBUG DebugService.Instance.DeleteMark(name); #endif } [MethodImpl(MethodImplOptions.AggressiveInlining)] public static void ProfileMarkBegin(int id) { -#if !DISABLE_ECS_DEBUG +#if !DISABLE_DRAGONECS_DEBUG DebugService.Instance.ProfileMarkBegin(id); #endif } [MethodImpl(MethodImplOptions.AggressiveInlining)] public static void ProfileMarkEnd(int id) { -#if !DISABLE_ECS_DEBUG +#if !DISABLE_DRAGONECS_DEBUG DebugService.Instance.ProfileMarkEnd(id); #endif } @@ -145,7 +145,7 @@ namespace DCFApixels.DragonECS public DefaultDebugService() { -#if !DISABLE_ECS_DEBUG +#if !DISABLE_DRAGONECS_DEBUG _stopwatchs = new Stopwatch[64]; _stopwatchsNames= new string[64]; #endif diff --git a/src/EcsFilter.cs b/src/EcsFilter.cs index 13beba7..a2029e2 100644 --- a/src/EcsFilter.cs +++ b/src/EcsFilter.cs @@ -15,7 +15,7 @@ namespace DCFApixels.DragonECS public class EcsFilter : IEcsFilter { private readonly IEcsWorld _source; - private readonly EcsGroup _entities; + internal readonly EcsGroup entities; private readonly EcsMask _mask; #region Properties @@ -32,12 +32,12 @@ namespace DCFApixels.DragonECS public EcsReadonlyGroup Entities { [MethodImpl(MethodImplOptions.AggressiveInlining)] - get => _entities.Readonly; + get => entities.Readonly; } public int EntitiesCount { [MethodImpl(MethodImplOptions.AggressiveInlining)] - get => _entities.Count; + get => entities.Count; } #endregion @@ -46,7 +46,7 @@ namespace DCFApixels.DragonECS { _source = source; _mask = mask; - _entities = new EcsGroup(source, capasity); + entities = new EcsGroup(source, capasity); } #endregion @@ -54,19 +54,19 @@ namespace DCFApixels.DragonECS [MethodImpl(MethodImplOptions.AggressiveInlining)] internal void Add(int entityID) { - _entities.Add(entityID); + entities.UncheckedAdd(entityID); } [MethodImpl(MethodImplOptions.AggressiveInlining)] internal void Remove(int entityID) { - _entities.Remove(entityID); + entities.UncheckedRemove(entityID); } #endregion #region GetEnumerator [MethodImpl(MethodImplOptions.AggressiveInlining)] - public EcsGroup.Enumerator GetEnumerator() => _entities.GetEnumerator(); + public EcsGroup.Enumerator GetEnumerator() => entities.GetEnumerator(); #endregion } } diff --git a/src/EcsGroup.cs b/src/EcsGroup.cs index d6353f1..2c5955b 100644 --- a/src/EcsGroup.cs +++ b/src/EcsGroup.cs @@ -17,6 +17,16 @@ namespace DCFApixels.DragonECS [MethodImpl(MethodImplOptions.AggressiveInlining)] get => _source.Count; } + public int CapacityDense + { + [MethodImpl(MethodImplOptions.AggressiveInlining)] + get => _source.CapacityDense; + } + public int CapacitySparce + { + [MethodImpl(MethodImplOptions.AggressiveInlining)] + get => _source.CapacitySparce; + } [MethodImpl(MethodImplOptions.AggressiveInlining)] public bool Contains(int entityID) => _source.Contains(entityID); [MethodImpl(MethodImplOptions.AggressiveInlining)] @@ -51,6 +61,16 @@ namespace DCFApixels.DragonECS [MethodImpl(MethodImplOptions.AggressiveInlining)] get => _count; } + public int CapacityDense + { + [MethodImpl(MethodImplOptions.AggressiveInlining)] + get => _dense.Length; + } + public int CapacitySparce + { + [MethodImpl(MethodImplOptions.AggressiveInlining)] + get => _sparse.Length; + } public EcsReadonlyGroup Readonly { [MethodImpl(MethodImplOptions.AggressiveInlining)] @@ -60,11 +80,29 @@ namespace DCFApixels.DragonECS #region Constrcutors [MethodImpl(MethodImplOptions.AggressiveInlining)] - public EcsGroup(IEcsWorld source, int denseCapacity = 64, int sparseCapacity = 256, int delayedOpsCapacity = 128) + internal EcsGroup(IEcsWorld source, int denseCapacity, int sparceCapacity, int delayedOpsCapacity) { _source = source; + source.RegisterGroup(this); _dense = new int[denseCapacity]; - _sparse = new int[sparseCapacity]; + _sparse = new int[sparceCapacity]; + + _delayedOps = new delayedOp[delayedOpsCapacity]; + + _lockCount = 0; + _delayedOpsCount = 0; + + _count = 0; + } + + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public EcsGroup(IEcsWorld source, int denseCapacity = 64, int delayedOpsCapacity = 128) + { + _source = source; + source.RegisterGroup(this); + _dense = new int[denseCapacity]; + _sparse = new int[source.Entities.CapacitySparce]; _delayedOps = new delayedOp[delayedOpsCapacity]; @@ -79,53 +117,64 @@ namespace DCFApixels.DragonECS [MethodImpl(MethodImplOptions.AggressiveInlining)] public bool Contains(int entityID) { - return /*entityID > 0 && */ entityID < _sparse.Length && _sparse[entityID] > 0; + //TODO добавить проверку на больше 0 в #if (DEBUG && !DISABLE_DRAGONECS_DEBUG) || !DRAGONECS_NO_SANITIZE_CHECKS +#if (DEBUG && !DISABLE_DRAGONECS_DEBUG) || !DRAGONECS_NO_SANITIZE_CHECKS +#endif + return /*entityID > 0 && entityID < _sparse.Length && */ _sparse[entityID] > 0; + } + #endregion + + #region IndexOf + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public int IndexOf(int entityID) + { + return _sparse[entityID]; } #endregion #region add/remove + public void UncheckedAdd(int entityID) => AddInternal(entityID); public void Add(int entityID) { - if (_lockCount > 0) + if (Contains(entityID)) return; + Add(entityID); + } + private void AddInternal(int entityID) + { + if (_lockCount > 0) { AddDelayedOp(entityID, DEALAYED_ADD); return; } - if (Contains(entityID)) - return; - - if(++_count >= _dense.Length) + if (++_count >= _dense.Length) Array.Resize(ref _dense, _dense.Length << 1); - if (entityID >= _sparse.Length) - { - int neadedSpace = _sparse.Length; - while (entityID >= neadedSpace) - neadedSpace <<= 1; - Array.Resize(ref _sparse, neadedSpace); - } - _dense[_count] = entityID; _sparse[entityID] = _count; } + public void UncheckedRemove(int entityID) => RemoveInternal(entityID); public void Remove(int entityID) + { + if (!Contains(entityID)) return; + RemoveInternal(entityID); + } + [MethodImpl(MethodImplOptions.AggressiveInlining)] + private void RemoveInternal(int entityID) { if (_lockCount > 0) { AddDelayedOp(entityID, DEALAYED_REMOVE); return; } - - if (!Contains(entityID)) - return; - _dense[_sparse[entityID]] = _dense[_count]; _sparse[_dense[_count--]] = _sparse[entityID]; _sparse[entityID] = 0; } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] private void AddDelayedOp(int entityID, int isAddBitFlag) { @@ -137,24 +186,32 @@ namespace DCFApixels.DragonECS } #endregion - //TODO добавить автосоритровку при каждом GetEnumerator + internal void OnWorldResize(int newSize) + { + Array.Resize(ref _sparse, newSize); + } + + //TODO добавить метод Sort + + //TODO добавить автосоритровку при каждом GetEnumerator и проверить будет ли прирост производительности или ее падение. + //Суть в том что так возможно можно будет более плотно подавать данные в проц #region AddGroup/RemoveGroup public void AddGroup(EcsReadonlyGroup group) { - foreach (var item in group) Add(item.id); + foreach (var item in group) UncheckedAdd(item.id); } public void RemoveGroup(EcsReadonlyGroup group) { - foreach (var item in group) Remove(item.id); + foreach (var item in group) UncheckedRemove(item.id); } public void AddGroup(EcsGroup group) { - foreach (var item in group) Add(item.id); + foreach (var item in group) UncheckedAdd(item.id); } public void RemoveGroup(EcsGroup group) { - foreach (var item in group) Remove(item.id); + foreach (var item in group) UncheckedRemove(item.id); } #endregion @@ -162,7 +219,7 @@ namespace DCFApixels.DragonECS [MethodImpl(MethodImplOptions.AggressiveInlining)] private void Unlock() { -#if DEBUG || !DRAGONECS_NO_SANITIZE_CHECKS +#if (DEBUG && !DISABLE_DRAGONECS_DEBUG) || !DRAGONECS_NO_SANITIZE_CHECKS if (_lockCount <= 0) { throw new Exception($"Invalid lock-unlock balance for {nameof(EcsGroup)}."); @@ -175,11 +232,11 @@ namespace DCFApixels.DragonECS delayedOp op = _delayedOps[i]; if (op >= 0) //delayedOp.IsAdded { - Add(op & int.MaxValue); //delayedOp.Entity + UncheckedAdd(op & int.MaxValue); //delayedOp.Entity } else { - Remove(op & int.MaxValue); //delayedOp.Entity + UncheckedRemove(op & int.MaxValue); //delayedOp.Entity } } } @@ -214,7 +271,6 @@ namespace DCFApixels.DragonECS { using (_marker.Auto()) return _source.World.GetEntity(_source._dense[_pointer]); - // return _source._dense[_pointer]; } } @@ -238,4 +294,19 @@ namespace DCFApixels.DragonECS } #endregion } + + + public static class EcsGroupExtensions + { + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static void Normalize(this EcsGroup self, ref T[] array) + { + if (array.Length < self.CapacityDense) Array.Resize(ref array, self.CapacityDense); + } + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static void Normalize(this EcsReadonlyGroup self, ref T[] array) + { + if (array.Length < self.CapacityDense) Array.Resize(ref array, self.CapacityDense); + } + } } diff --git a/src/EcsPipeline.cs b/src/EcsPipeline.cs index 2e395ac..ef54783 100644 --- a/src/EcsPipeline.cs +++ b/src/EcsPipeline.cs @@ -7,6 +7,21 @@ namespace DCFApixels.DragonECS { public sealed class EcsPipeline { + private static EcsPipeline _empty; + public static EcsPipeline Empty + { + get + { + if(_empty == null) + { + _empty = new EcsPipeline(Array.Empty()); + _empty.Init(); + _empty._isEmptyDummy = true; + } + return _empty; + } + } + private IEcsSystem[] _allSystems; private Dictionary _runners; private IEcsRunSystem _runRunnerCache; @@ -16,10 +31,12 @@ namespace DCFApixels.DragonECS private bool _isInit; private bool _isDestoryed; + private bool _isEmptyDummy; #region Properties public ReadOnlyCollection AllSystems => _allSystemsSealed; public ReadOnlyDictionary AllRunners => _allRunnersSealed; + public bool IsInit => _isInit; public bool IsDestoryed => _isDestoryed; #endregion @@ -33,6 +50,7 @@ namespace DCFApixels.DragonECS _allRunnersSealed = new ReadOnlyDictionary(_runners); _isInit = false; + _isEmptyDummy = false; _isDestoryed = false; } #endregion @@ -57,7 +75,10 @@ namespace DCFApixels.DragonECS #region LifeCycle public void Init() { - if(_isInit == true) + if (_isEmptyDummy) + return; + + if (_isInit == true) { EcsDebug.Print("[Warning]", $"This {nameof(EcsPipeline)} has already been initialized"); return; @@ -80,7 +101,7 @@ namespace DCFApixels.DragonECS [MethodImpl(MethodImplOptions.AggressiveInlining)] public void Run() { -#if DEBUG || !DRAGONECS_NO_SANITIZE_CHECKS +#if (DEBUG && !DISABLE_DRAGONECS_DEBUG) || !DRAGONECS_NO_SANITIZE_CHECKS CheckBeforeInitForMethod(nameof(Run)); CheckAfterDestroyForMethod(nameof(Run)); #endif @@ -88,7 +109,10 @@ namespace DCFApixels.DragonECS } public void Destroy() { -#if DEBUG || !DRAGONECS_NO_SANITIZE_CHECKS + if (_isEmptyDummy) + return; + +#if (DEBUG && !DISABLE_DRAGONECS_DEBUG) || !DRAGONECS_NO_SANITIZE_CHECKS CheckBeforeInitForMethod(nameof(Run)); #endif if (_isDestoryed == true) @@ -102,7 +126,7 @@ namespace DCFApixels.DragonECS #endregion #region StateChecks -#if DEBUG || !DRAGONECS_NO_SANITIZE_CHECKS +#if (DEBUG && !DISABLE_DRAGONECS_DEBUG) || !DRAGONECS_NO_SANITIZE_CHECKS private void CheckBeforeInitForMethod(string methodName) { if (!_isInit) diff --git a/src/EcsPool.cs b/src/EcsPool.cs index e25c161..a726cae 100644 --- a/src/EcsPool.cs +++ b/src/EcsPool.cs @@ -1,6 +1,8 @@ using System; using System.Linq; using System.Runtime.CompilerServices; +using System.Runtime.InteropServices; +using Unity.Profiling; namespace DCFApixels.DragonECS { @@ -15,6 +17,8 @@ namespace DCFApixels.DragonECS public bool Has(int index); public void Write(int index); public void Del(int index); + + internal void OnWorldResize(int newSize); } public interface IEcsPool : IEcsPool @@ -43,6 +47,8 @@ namespace DCFApixels.DragonECS public void Del(int index) { } public bool Has(int index) => false; public void Write(int index) { } + + void IEcsPool.OnWorldResize(int newSize) { } } public class EcsPool : IEcsPool @@ -50,14 +56,21 @@ namespace DCFApixels.DragonECS { private readonly int _id; private readonly IEcsWorld _source; - private readonly SparseSet _sparseSet; - private T[] _denseItems; + // private readonly EcsGroup _entities; + + private int[] _mapping;// index = entity / value = itemIndex;/ value = 0 = no entity + private T[] _items; //dense + private int _itemsCount; + private int[] _recycledItems; + private int _recycledItemsCount; private IEcsComponentReset _componentResetHandler; + private PoolRunnres _poolRunnres; + #region Properites - public int EntitiesCount => _sparseSet.Count; - public int Capacity => _sparseSet.CapacityDense; + public int EntitiesCount => _itemsCount; + public int Capacity => _items.Length; public IEcsWorld World => _source; public Type DataType => typeof(T); @@ -65,47 +78,81 @@ namespace DCFApixels.DragonECS #endregion #region Constructors - public EcsPool(IEcsWorld source, int id, int capacity) + internal EcsPool(IEcsWorld source, int id, int capacity, PoolRunnres poolRunnres) { _source = source; _id = id; - _sparseSet = new SparseSet(capacity, capacity); - _denseItems =new T[capacity]; + _mapping = new int[source.EntitesCapacity]; + _recycledItems = new int[128]; + _recycledItemsCount = 0; + _items =new T[capacity]; + _itemsCount = 0; _componentResetHandler = ComponentResetHandler.New(); + _poolRunnres = poolRunnres; } #endregion - #region Read/Write/Has/Del - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public ref readonly T Read(int entity) - { - return ref _denseItems[_sparseSet.IndexOf(entity)]; - } - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public ref T Write(int entity) - { - if (!_sparseSet.Contains(entity)) - { - _sparseSet.Add(entity); - _sparseSet.Normalize(ref _denseItems); - _source.OnEntityComponentAdded(entity, _id); - _componentResetHandler.Reset(ref _denseItems[_sparseSet.IndexOf(entity)]); - } - return ref _denseItems[_sparseSet.IndexOf(entity)]; - } + #region Write/Read/Has/Del - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public bool Has(int entity) + private ProfilerMarker _writeMark = new ProfilerMarker("EcsPoo.Write"); + private ProfilerMarker _readMark = new ProfilerMarker("EcsPoo.Read"); + private ProfilerMarker _hasMark = new ProfilerMarker("EcsPoo.Has"); + private ProfilerMarker _delMark = new ProfilerMarker("EcsPoo.Del"); + public ref T Write(int entityID) { - return _sparseSet.IndexOf(entity) >= 0; + //using (_writeMark.Auto()) + //{ + ref int itemIndex = ref _mapping[entityID]; + if (itemIndex <= 0) // 0 + { + if (_recycledItemsCount > 0) + { + itemIndex = _recycledItems[--_recycledItemsCount]; + _itemsCount++; + } + else + { + itemIndex = _itemsCount++; + if (itemIndex >= _items.Length) + Array.Resize(ref _items, _items.Length << 1); + } + _mapping[entityID] = itemIndex; + _componentResetHandler.Reset(ref _items[itemIndex]); + _source.OnEntityComponentAdded(entityID, _id); + _poolRunnres.add.OnComponentAdd(entityID); + } + + _poolRunnres.write.OnComponentWrite(entityID); + return ref _items[itemIndex]; + // } } [MethodImpl(MethodImplOptions.AggressiveInlining)] - public void Del(int entity) + public ref readonly T Read(int entityID) { - _sparseSet.RemoveAt(entity); - _source.OnEntityComponentRemoved(entity, _id); + //using (_readMark.Auto()) + return ref _items[_mapping[entityID]]; + } + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public bool Has(int entityID) + { + //using (_hasMark.Auto()) + return _mapping[entityID] > 0; + } + public void Del(int entityID) + { + //using (_delMark.Auto()) + //{ + if(_recycledItemsCount >= _recycledItems.Length) + Array.Resize(ref _recycledItems, _recycledItems.Length << 1); + _recycledItems[_recycledItemsCount++] = _mapping[entityID]; + _mapping[entityID] = 0; + _itemsCount--; + //_entities.UncheckedRemove(entityID); + _source.OnEntityComponentRemoved(entityID, _id); + _poolRunnres.del.OnComponentDel(entityID); + //} } #endregion @@ -123,33 +170,10 @@ namespace DCFApixels.DragonECS } public override int GetHashCode() => _source.GetHashCode() + ID; #endregion - } - #region ComponentResetHandler - internal static class ComponentResetHandler - { - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static IEcsComponentReset New() + void IEcsPool.OnWorldResize(int newSize) { - Type targetType = typeof(T); - if (targetType.GetInterfaces().Contains(typeof(IEcsComponentReset<>).MakeGenericType(targetType))) - { - return (IEcsComponentReset)Activator.CreateInstance(typeof(ComponentResetHandler<>).MakeGenericType(targetType)); - } - return (IEcsComponentReset)Activator.CreateInstance(typeof(ComponentResetDummy<>).MakeGenericType(targetType)); + Array.Resize(ref _mapping, newSize); } } - internal sealed class ComponentResetDummy : IEcsComponentReset - { - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public void Reset(ref T component) => component = default; - } - internal sealed class ComponentResetHandler : IEcsComponentReset - where T : IEcsComponentReset - { - private T _fakeInstnace = default; - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public void Reset(ref T component) => _fakeInstnace.Reset(ref component); - } - #endregion } diff --git a/src/EcsRunner.cs b/src/EcsRunner.cs index 6233e42..fb13b51 100644 --- a/src/EcsRunner.cs +++ b/src/EcsRunner.cs @@ -51,7 +51,7 @@ namespace DCFApixels.DragonECS .Where(type => type.BaseType != null && type.BaseType.IsGenericType && runnerBaseType == type.BaseType.GetGenericTypeDefinition())); } -#if DEBUG || !DRAGONECS_NO_SANITIZE_CHECKS +#if (DEBUG && !DISABLE_DRAGONECS_DEBUG) || !DRAGONECS_NO_SANITIZE_CHECKS for (int i = 0; i < runnerHandlerTypes.Count; i++) { var e = CheckRunnerValide(runnerHandlerTypes[i]); @@ -136,7 +136,7 @@ namespace DCFApixels.DragonECS private static Type _subclass; internal static void Register(Type subclass) { -#if DEBUG || !DRAGONECS_NO_SANITIZE_CHECKS +#if (DEBUG && !DISABLE_DRAGONECS_DEBUG) || !DRAGONECS_NO_SANITIZE_CHECKS if (_subclass != null) { throw new EcsRunnerImplementationException($"The Runner<{typeof(TInterface).FullName}> can have only one implementing subclass"); diff --git a/src/EcsWorld.cs b/src/EcsWorld.cs index 5c29815..5989a0d 100644 --- a/src/EcsWorld.cs +++ b/src/EcsWorld.cs @@ -2,6 +2,8 @@ using System.Collections.Generic; using System.Collections.ObjectModel; using System.Runtime.CompilerServices; +using System.Runtime.InteropServices; +using System.Xml; namespace DCFApixels.DragonECS { @@ -12,9 +14,12 @@ namespace DCFApixels.DragonECS public bool IsEmpty { get; } public Type ArchetypeType { get; } public int ID { get; } + public EcsPipeline Pipeline { get; } + public int EntitesCount { get; } + public int EntitesCapacity { get; } + public EcsReadonlyGroup Entities { get; } #endregion - #region GetterMethods public ReadOnlySpan GetAllPools(); @@ -22,12 +27,13 @@ namespace DCFApixels.DragonECS #region Methods public EcsPool GetPool() where T : struct; + public EcsPool UncheckedGetPool() where T : struct; public EcsFilter Filter() where TInc : struct, IInc; public EcsFilter Filter() where TInc : struct, IInc where TExc : struct, IExc; public ent NewEntity(); + public void DelEntity(ent entity); public bool EntityIsAlive(int entityID, short gen); public ent GetEntity(int entityID); - public void DelEntity(int entityID); public void Destroy(); public bool IsMaskCompatible(EcsMask mask, int entity); @@ -35,6 +41,10 @@ namespace DCFApixels.DragonECS internal void OnEntityComponentAdded(int entityID, int changedPoolID); internal void OnEntityComponentRemoved(int entityID, int changedPoolID); + + public int GetComponentID(); + + internal void RegisterGroup(EcsGroup group); #endregion } @@ -48,6 +58,8 @@ namespace DCFApixels.DragonECS public EcsWorld() { id = (short)_worldIdDispenser.GetFree(); + if(id >= Worlds.Length) + Array.Resize(ref Worlds, Worlds.Length << 1); Worlds[id] = (IEcsWorld)this; } @@ -75,8 +87,19 @@ namespace DCFApixels.DragonECS private EcsFilter[] _filters; + private EcsPipeline _pipeline; + + private List _groups; + + #region RunnersCache + private PoolRunnres _poolRunnres; + private IEcsEntityCreate _entityCreate; + private IEcsEntityDestroy _entityDestry; + #endregion + #region GetterMethods public ReadOnlySpan GetAllPools() => new ReadOnlySpan(_pools); + public int GetComponentID() => ComponentType.uniqueID; #endregion @@ -84,21 +107,38 @@ namespace DCFApixels.DragonECS public bool IsEmpty => _entities.Count < 0; public Type ArchetypeType => typeof(TArchetype); public int ID => id; + public EcsPipeline Pipeline => _pipeline; + + public int EntitesCount => _entities.Count; + public int EntitesCapacity => _entities.CapacityDense; + public EcsReadonlyGroup Entities => _entities.Readonly; #endregion #region Constructors - public EcsWorld() + public EcsWorld(EcsPipeline pipline = null) { + _pipeline = pipline ?? EcsPipeline.Empty; + if (!_pipeline.IsInit) pipline.Init(); _entityDispenser = new IntDispenser(1); _nullPool = new EcsNullPool(this); _pools = new IEcsPool[512]; FillArray(_pools, _nullPool); - //Array.Fill(_pools, _nullPool); //TODO Fix it + _gens = new short[512]; _filters = new EcsFilter[64]; - _entities = new EcsGroup(this, 512); + _groups = new List(128); + + _entities = new EcsGroup(this, 512, 512, 0); + _filtersByIncludedComponents = new List[16]; _filtersByExcludedComponents = new List[16]; + + _poolRunnres = new PoolRunnres(_pipeline); + _entityCreate = _pipeline.GetRunner(); + _entityDestry = _pipeline.GetRunner(); + _pipeline.GetRunner>().Inject((TArchetype)this); + _pipeline.GetRunner>().Inject(this); + _pipeline.GetRunner().OnWorldCreate(this); } #endregion @@ -120,14 +160,17 @@ namespace DCFApixels.DragonECS if (_pools[uniqueID] == _nullPool) { - _pools[uniqueID] = new EcsPool(this, ComponentType.uniqueID, 512); + _pools[uniqueID] = new EcsPool(this, ComponentType.uniqueID, 512, _poolRunnres); } return (EcsPool)_pools[uniqueID]; } - + public EcsPool UncheckedGetPool() where T : struct + { + return (EcsPool)_pools[ComponentType.uniqueID]; + } #endregion - #region GetFilter + #region GetFilter public EcsFilter Filter() where TInc : struct, IInc => Filter(); public EcsFilter Filter() where TInc : struct, IInc where TExc : struct, IExc { @@ -181,7 +224,6 @@ namespace DCFApixels.DragonECS } } - return filter; } #endregion @@ -189,7 +231,7 @@ namespace DCFApixels.DragonECS #region IsMaskCompatible/IsMaskCompatibleWithout public bool IsMaskCompatible(EcsMask mask, int entity) { -#if DEBUG || !DRAGONECS_NO_SANITIZE_CHECKS +#if (DEBUG && !DISABLE_DRAGONECS_DEBUG) || !DRAGONECS_NO_SANITIZE_CHECKS if (mask.WorldArchetypeType != typeof(TArchetype)) throw new EcsFrameworkException("mask.WorldArchetypeType != typeof(TArchetype)"); #endif @@ -208,7 +250,7 @@ namespace DCFApixels.DragonECS public bool IsMaskCompatibleWithout(EcsMask mask, int entity, int otherComponentID) { -#if DEBUG || !DRAGONECS_NO_SANITIZE_CHECKS +#if (DEBUG && !DISABLE_DRAGONECS_DEBUG) || !DRAGONECS_NO_SANITIZE_CHECKS if (mask.WorldArchetypeType != typeof(TArchetype)) throw new EcsFrameworkException("mask.WorldArchetypeType != typeof(TArchetype)"); #endif @@ -220,8 +262,8 @@ namespace DCFApixels.DragonECS } for (int i = 0, iMax = mask.ExcCount; i < iMax; i++) { - int poolID = mask.Exc[i]; - if (poolID != otherComponentID && _pools[poolID].Has(entity)) + int componentID = mask.Exc[i]; + if (componentID != otherComponentID && _pools[componentID].Has(entity)) return false; } return true; @@ -234,53 +276,59 @@ namespace DCFApixels.DragonECS var includeList = _filtersByIncludedComponents[componentID]; var excludeList = _filtersByExcludedComponents[componentID]; - if (includeList != null) - { - foreach (var filter in includeList) - { - if (IsMaskCompatible(filter.Mask, entityID)) - { - filter.Add(entityID); - } - } - } - if (excludeList != null) - { - foreach (var filter in excludeList) - { - if (IsMaskCompatibleWithout(filter.Mask, entityID, componentID)) - { - filter.Remove(entityID); - } - } - } + //if (includeList != null) + //{ + // foreach (var filter in includeList) + // { + // if (IsMaskCompatible(filter.Mask, entityID)) + // { + // filter.Add(entityID); + // } + // } + //} + //if (excludeList != null) + //{ + // foreach (var filter in excludeList) + // { + // if (IsMaskCompatibleWithout(filter.Mask, entityID, componentID)) + // { + // filter.Remove(entityID); + // } + // } + //} + + if (includeList != null) foreach (var filter in includeList) filter.entities.Add(entityID); + if (excludeList != null) foreach (var filter in excludeList) filter.entities.Remove(entityID); } - void IEcsWorld.OnEntityComponentRemoved(int entityID, int changedPoolID) + void IEcsWorld.OnEntityComponentRemoved(int entityID, int componentID) { - var includeList = _filtersByIncludedComponents[changedPoolID]; - var excludeList = _filtersByExcludedComponents[changedPoolID]; + var includeList = _filtersByIncludedComponents[componentID]; + var excludeList = _filtersByExcludedComponents[componentID]; - if (includeList != null) - { - foreach (var filter in includeList) - { - if (IsMaskCompatible(filter.Mask, entityID)) - { - filter.Remove(entityID); - } - } - } - if (excludeList != null) - { - foreach (var filter in excludeList) - { - if (IsMaskCompatibleWithout(filter.Mask, entityID, changedPoolID)) - { - filter.Add(entityID); - } - } - } + //if (includeList != null) + //{ + // foreach (var filter in includeList) + // { + // if (IsMaskCompatible(filter.Mask, entityID)) + // { + // filter.Remove(entityID); + // } + // } + //} + //if (excludeList != null) + //{ + // foreach (var filter in excludeList) + // { + // if (IsMaskCompatibleWithout(filter.Mask, entityID, componentID)) + // { + // filter.Add(entityID); + // } + // } + //} + + if (includeList != null) foreach (var filter in includeList) filter.entities.Remove(entityID); + if (excludeList != null) foreach (var filter in excludeList) filter.entities.Add(entityID); } #endregion @@ -288,23 +336,37 @@ namespace DCFApixels.DragonECS public ent NewEntity() { int entityID = _entityDispenser.GetFree(); - _entities.Add(entityID); - if (_gens.Length <= entityID) + _entities.UncheckedAdd(entityID); + if (_gens.Length <= entityID) + { Array.Resize(ref _gens, _gens.Length << 1); - return new ent(entityID, _gens[entityID]++, id); + _entities.OnWorldResize(_gens.Length); + foreach (var item in _groups) + { + item.OnWorldResize(_gens.Length); + } + foreach (var item in _pools) + { + item.OnWorldResize(_gens.Length); + } + } + + ent entity = new ent(entityID, _gens[entityID]++, id); + _entityCreate.OnEntityCreate(entity); + return entity; } + public void DelEntity(ent entity) + { + _entityDispenser.Release(entity.id); + _entities.UncheckedRemove(entity.id); + _entityDestry.OnEntityDestroy(entity); + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] public ent GetEntity(int entityID) { - if (_entities.Contains(entityID) == false) - return ent.NULL; - return new ent(entityID, _gens[entityID], id); } - public void DelEntity(int entityID) - { - _entityDispenser.Release(entityID); - _entities.Remove(entityID); - } [MethodImpl(MethodImplOptions.AggressiveInlining)] public bool EntityIsAlive(int entityID, short gen) { @@ -325,6 +387,11 @@ namespace DCFApixels.DragonECS _filters = null; Realeze(); } + public void DestryWithPipeline() + { + Destroy(); + _pipeline.Destroy(); + } #endregion #region Utils @@ -344,7 +411,7 @@ namespace DCFApixels.DragonECS static ComponentType() { uniqueID = ComponentType.increment++; -#if DEBUG || !DRAGONECS_NO_SANITIZE_CHECKS +#if (DEBUG && !DISABLE_DRAGONECS_DEBUG) || !DRAGONECS_NO_SANITIZE_CHECKS if (ComponentType.increment + 1 > ushort.MaxValue) { throw new EcsFrameworkException($"No more room for new component for this {typeof(TArchetype).FullName} IWorldArchetype"); @@ -374,15 +441,27 @@ namespace DCFApixels.DragonECS } } #endregion - } - #region Extensions - public static class IEcsWorldExtensions - { - public static void DelEntity(this IEcsWorld self, ent entity) + #region Other + void IEcsWorld.RegisterGroup(EcsGroup group) { - self.DelEntity(entity.id); + _groups.Add(group); + } + #endregion + } + + [StructLayout(LayoutKind.Sequential, Pack = 8, Size = 24)] + internal readonly struct PoolRunnres + { + public readonly IEcsComponentAdd add; + public readonly IEcsComponentWrite write; + public readonly IEcsComponentDel del; + + public PoolRunnres(EcsPipeline pipeline) + { + add = pipeline.GetRunner(); + write = pipeline.GetRunner(); + del = pipeline.GetRunner(); } } - #endregion } diff --git a/src/Utils/ComponentResetHandler.cs b/src/Utils/ComponentResetHandler.cs new file mode 100644 index 0000000..20a0210 --- /dev/null +++ b/src/Utils/ComponentResetHandler.cs @@ -0,0 +1,32 @@ +using System; +using System.Linq; +using System.Runtime.CompilerServices; + +namespace DCFApixels.DragonECS +{ + internal static class ComponentResetHandler + { + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static IEcsComponentReset New() + { + Type targetType = typeof(T); + if (targetType.GetInterfaces().Contains(typeof(IEcsComponentReset<>).MakeGenericType(targetType))) + { + return (IEcsComponentReset)Activator.CreateInstance(typeof(ComponentResetHandler<>).MakeGenericType(targetType)); + } + return (IEcsComponentReset)Activator.CreateInstance(typeof(ComponentResetDummy<>).MakeGenericType(targetType)); + } + } + internal sealed class ComponentResetDummy : IEcsComponentReset + { + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public void Reset(ref T component) => component = default; + } + internal sealed class ComponentResetHandler : IEcsComponentReset + where T : IEcsComponentReset + { + private T _fakeInstnace = default; + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public void Reset(ref T component) => _fakeInstnace.Reset(ref component); + } +} diff --git a/src/ent.cs b/src/ent.cs index aadd8e6..4193c20 100644 --- a/src/ent.cs +++ b/src/ent.cs @@ -97,18 +97,18 @@ namespace DCFApixels.DragonECS [MethodImpl(MethodImplOptions.AggressiveInlining)] public static bool IsAlive(this ref ent self) { - using (_IsAliveMarker.Auto()) - { + //using (_IsAliveMarker.Auto()) + //{ bool result = EcsWorld.Worlds[self.world].EntityIsAlive(self.id, self.gen); if (!result) self = ent.NULL; return result; - } + //} } [MethodImpl(MethodImplOptions.AggressiveInlining)] public static bool IsNull(this in ent self) { - using (_IsNullMarker.Auto()) + //using (_IsNullMarker.Auto()) return self == ent.NULL; } @@ -116,28 +116,28 @@ namespace DCFApixels.DragonECS public static ref readonly T Read(this in ent self) where T : struct { - using (_ReadMarker.Auto()) + //using (_ReadMarker.Auto()) return ref EcsWorld.Worlds[self.world].GetPool().Read(self.id); } [MethodImpl(MethodImplOptions.AggressiveInlining)] public static ref T Write(this in ent self) where T : struct { - using (_WriteMarker.Auto()) + //using (_WriteMarker.Auto()) return ref EcsWorld.Worlds[self.world].GetPool().Write(self.id); } [MethodImpl(MethodImplOptions.AggressiveInlining)] public static bool Has(this in ent self) where T : struct { - using (_HasMarker.Auto()) + //using (_HasMarker.Auto()) return EcsWorld.Worlds[self.world].GetPool().Has(self.id); } [MethodImpl(MethodImplOptions.AggressiveInlining)] public static void Del(this in ent self) where T : struct { - using (_DelMarker.Auto()) + //using (_DelMarker.Auto()) EcsWorld.Worlds[self.world].GetPool().Del(self.id); } }