From 66b136df92551a014a173adec250dd1a2cfcbe4e Mon Sep 17 00:00:00 2001 From: Mikhail <99481254+DCFApixels@users.noreply.github.com> Date: Sat, 11 Mar 2023 17:11:40 +0800 Subject: [PATCH] Rework Rework runner system, SparseSet etc --- src/Builtin/FiltersProcessor.cs | 15 - src/Builtin/InjectProcessor.cs | 3 +- src/EcsGroup.cs | 4 +- src/EcsPool.cs | 26 +- src/EcsSession.cs | 57 ++-- src/{IEcsWorld.cs => EcsWorld.cs} | 64 +++-- src/{IEcsWorld.cs.meta => EcsWorld.cs.meta} | 0 src/INFO.txt | 5 +- src/Interfaces/IEcsProcessor.cs | 27 +- src/Primitives/Entity.cs | 77 ++++- src/React/EcsProcessorsGMessenger.cs | 41 --- src/React/EcsProcessorsMessenger.cs | 4 + src/React/EcsProcessorsRunner.cs | 1 - src/React/RunnerHandler.cs | 126 ++++++++ ...essenger.cs.meta => RunnerHandler.cs.meta} | 2 +- src/Utils/IntDispenser.cs | 44 +++ .../IntDispenser.cs.meta} | 2 +- src/Utils/SparseSet.cs | 272 ++++++++---------- test/Startup.cs | 1 - 19 files changed, 449 insertions(+), 322 deletions(-) delete mode 100644 src/Builtin/FiltersProcessor.cs rename src/{IEcsWorld.cs => EcsWorld.cs} (82%) rename src/{IEcsWorld.cs.meta => EcsWorld.cs.meta} (100%) delete mode 100644 src/React/EcsProcessorsGMessenger.cs create mode 100644 src/React/RunnerHandler.cs rename src/React/{EcsProcessorsGMessenger.cs.meta => RunnerHandler.cs.meta} (83%) create mode 100644 src/Utils/IntDispenser.cs rename src/{Builtin/FiltersProcessor.cs.meta => Utils/IntDispenser.cs.meta} (83%) diff --git a/src/Builtin/FiltersProcessor.cs b/src/Builtin/FiltersProcessor.cs deleted file mode 100644 index 1cb620a..0000000 --- a/src/Builtin/FiltersProcessor.cs +++ /dev/null @@ -1,15 +0,0 @@ -namespace DCFApixels.DragonECS -{ - public class FiltersProcessor : IEcsGReceive<_OnComponentAdded>, IEcsGReceive<_OnComponentRemoved> - { - void IEcsGReceive<_OnComponentAdded>.Do(EcsSession session, in _OnComponentAdded message, in T obj) - { - - } - - void IEcsGReceive<_OnComponentRemoved>.Do(EcsSession session, in _OnComponentRemoved message, in T obj) - { - - } - } -} diff --git a/src/Builtin/InjectProcessor.cs b/src/Builtin/InjectProcessor.cs index 24b3a0f..e6800bf 100644 --- a/src/Builtin/InjectProcessor.cs +++ b/src/Builtin/InjectProcessor.cs @@ -17,9 +17,8 @@ namespace DCFApixels.DragonECS void IDo<_PreInit>.Do(EcsSession session) { - _OnInject m = new _OnInject(_injectedData); var messenger = session.GetMessenger<_OnInject>(); - messenger.Send(in m); + messenger.Send(new _OnInject(_injectedData)); messenger.Destroy(); } } diff --git a/src/EcsGroup.cs b/src/EcsGroup.cs index 198e63b..f780df4 100644 --- a/src/EcsGroup.cs +++ b/src/EcsGroup.cs @@ -34,13 +34,13 @@ namespace DCFApixels.DragonECS public EcsGroup(IEcsWorld world, int entitiesCapacity, int delayedOpsCapacity = 128) { _source = world; - _entities = new SparseSet(entitiesCapacity); + _entities = new SparseSet(entitiesCapacity, entitiesCapacity); _delayedOps = new DelayedOp[delayedOpsCapacity]; _lockCount = 0; } #endregion - #region add/remove + #region Add/Remove public void Add(int entityID) { if (_lockCount > 0) diff --git a/src/EcsPool.cs b/src/EcsPool.cs index c3dca5c..f7d47d3 100644 --- a/src/EcsPool.cs +++ b/src/EcsPool.cs @@ -40,7 +40,7 @@ namespace DCFApixels.DragonECS public EcsPool(IEcsWorld source, int capacity) { _source = source; - _sparseSet = new SparseSet(capacity); + _sparseSet = new SparseSet(capacity, capacity); _denseItems =new T[capacity]; } @@ -96,28 +96,4 @@ namespace DCFApixels.DragonECS public override int GetHashCode() => _source.GetHashCode() + ID; #endregion } - - public static partial class EntityExtensions - { - public static ref readonly T Read(this in Entity self) - where T : struct - { - return ref self.world.GetPool().Read(self.id); - } - public static ref T Write(this in Entity self) - where T : struct - { - return ref self.world.GetPool().Write(self.id); - } - public static bool Has(this in Entity self) - where T : struct - { - return self.world.GetPool().Has(self.id); - } - public static void Del(this in Entity self) - where T : struct - { - self.world.GetPool().Del(self.id); - } - } } diff --git a/src/EcsSession.cs b/src/EcsSession.cs index cab57b6..0731e3b 100644 --- a/src/EcsSession.cs +++ b/src/EcsSession.cs @@ -6,6 +6,34 @@ using System.Runtime.CompilerServices; namespace DCFApixels.DragonECS { + public class EcsWorldMap + { + private Dictionary<(Type, string), IEcsWorld> _worlds = new Dictionary<(Type, string), IEcsWorld>(8); + private bool _built = false; + + public void Add(EcsWorld world, string name = "") + where TArchetype : IWorldArchetype + { + if(_built) { throw new Exception($"Cant change built {nameof(EcsWorldMap)}"); } + _worlds.Add((typeof(TArchetype), name), world); + } + + public EcsWorld Get(string name ="") + where TArchetype : IWorldArchetype + { + return (EcsWorld)_worlds[(typeof(TArchetype), name)]; + } + + public IEcsWorld Get(Type type, string name = "") + { + return _worlds[(type, name)]; + } + + public void Build() + { + _built = true; + } + } public class EcsSession { @@ -18,14 +46,13 @@ namespace DCFApixels.DragonECS private bool _isInit = false; private bool _isDestoryed = false; - private int _worldIdIncrement; - private Dictionary _worldsDict = new Dictionary(); - private List _worlds = new List(); private Dictionary _runners; private Dictionary _messengers; private EcsProcessorsRunner<_Run> _runRunnerCache; + private EcsWorldMap _worldMap = new EcsWorldMap(); + #region Properties public ReadOnlyCollection AllProcessors => _allProcessorsSealed; @@ -33,7 +60,6 @@ namespace DCFApixels.DragonECS #region React Runners/Messengers public EcsProcessorsRunner GetRunner() - where TDoTag : IEcsDoTag { Type type = typeof(TDoTag); if (_runners.TryGetValue(type, out IEcsProcessorsRunner result)) @@ -45,7 +71,6 @@ namespace DCFApixels.DragonECS return (EcsProcessorsRunner)result; } internal void OnRunnerDetroyed(EcsProcessorsRunner target) - where TDoTag : IEcsDoTag { _runners.Remove(typeof(TDoTag)); } @@ -62,18 +87,6 @@ namespace DCFApixels.DragonECS _messengers.Add(type, result); return (EcsProcessorsMessenger)result; } - public EcsProcessorsGMessenger GetGMessenger() - where TMessege : IEcsMessage - { - Type type = typeof(EcsProcessorsGMessenger); - if (_messengers.TryGetValue(type, out IEcsProcessorsMessenger result)) - { - return (EcsProcessorsGMessenger)result; - } - result = new EcsProcessorsMessenger(this); - _messengers.Add(type, result); - return (EcsProcessorsGMessenger)result; - } internal void OnMessengerDetroyed(IEcsProcessorsMessenger target) where TMessege : IEcsMessage { @@ -85,7 +98,6 @@ namespace DCFApixels.DragonECS public EcsSession Add(IEcsProcessor system) { CheckInitForMethod(nameof(AddWorld)); - _allProcessors.Add(system); return this; } @@ -93,8 +105,7 @@ namespace DCFApixels.DragonECS where TArchetype : IWorldArchetype { CheckInitForMethod(nameof(AddWorld)); - - _worlds.Add(new EcsWorld(_worldIdIncrement++)); + _worldMap.Add(world, name); return this; } @@ -104,9 +115,12 @@ namespace DCFApixels.DragonECS public void Init() { CheckInitForMethod(nameof(Init)); + _worldMap.Build(); _allProcessorsSealed = _allProcessors.AsReadOnly(); _isInit = true; + GetMessenger<_OnInject>().Send(new _OnInject(_worldMap)); + GetRunner<_PreInit>().Run(); GetRunner<_Init>().Run(); @@ -158,6 +172,7 @@ namespace DCFApixels.DragonECS - + #region Utils + #endregion } } diff --git a/src/IEcsWorld.cs b/src/EcsWorld.cs similarity index 82% rename from src/IEcsWorld.cs rename to src/EcsWorld.cs index cd01122..643cbf7 100644 --- a/src/IEcsWorld.cs +++ b/src/EcsWorld.cs @@ -8,20 +8,16 @@ using System.Threading.Tasks; namespace DCFApixels.DragonECS { public interface IWorldArchetype { } - public struct DefaultArchetype : IWorldArchetype { } + public struct DefaultWorld : IWorldArchetype { } public interface IEcsWorld { - public const int MAX_WORLDS = byte.MaxValue; //Номер последнего мира 254 - public const int DEAD_WORLD_ID = byte.MaxValue; //Зарезервированный номер мира для мертвых сущьностей - //private float _timeScale;//TODO реализовать собсвенныйтайм склей для разных миров #region Properties - public ushort ID { get; internal set; } - public bool IsAlive { get; } public bool IsEmpty { get; } public Type ArchetypeType { get; } + public int ID { get; } #endregion public EcsPool GetPool() where T : struct; @@ -36,15 +32,29 @@ namespace DCFApixels.DragonECS internal void OnEntityComponentRemoved(int entityID, int changedPoolID); } - public class EcsWorld : IEcsWorld + + public abstract class EcsWorld + { + internal static IEcsWorld[] Worlds = new IEcsWorld[8]; + private static IntDispenser _worldIdDispenser = new IntDispenser(); + + public readonly short id; + + public EcsWorld() + { + id = (short)_worldIdDispenser.GetFree(); + Worlds[id] = (IEcsWorld)this; + } + } + + public sealed class EcsWorld : EcsWorld, IEcsWorld where TArchetype : IWorldArchetype { - private ushort _id = IEcsWorld.DEAD_WORLD_ID; + private IntDispenser _entityDispenser; + private EcsGroup _entities; - private SparseSet _componentIDToPoolID; - - private SparseSet _entities = new SparseSet(); private short[] _gens; + private short[] _componentCounts; private IEcsPool[] _pools; @@ -54,22 +64,18 @@ namespace DCFApixels.DragonECS private EcsFilter[] _filters; #region Properties - public ushort ID => _id; - ushort IEcsWorld.ID { get => _id; set => _id = value; } - - public bool IsAlive => _id != IEcsWorld.DEAD_WORLD_ID; public bool IsEmpty => _entities.Count < 0; public Type ArchetypeType => typeof(TArchetype); - + public int ID => id; #endregion #region Constructors public EcsWorld() { + _entityDispenser = new IntDispenser(); _pools = new IEcsPool[512]; - _entities = new SparseSet(512); - _componentIDToPoolID = new SparseSet(512); - _filters = new EcsFilter[512]; + _filters = new EcsFilter[64]; + _entities = new EcsGroup(this, 512); } #endregion @@ -117,14 +123,14 @@ namespace DCFApixels.DragonECS BakedMask bakedMask = mask.GetBaked(); for (int i = 0, iMax = bakedMask.IncCount; i < iMax; i++) { - if (!_pools[_componentIDToPoolID[bakedMask.Inc[i]]].Has(entity)) + if (!_pools[bakedMask.Inc[i]].Has(entity)) { return false; } } for (int i = 0, iMax = bakedMask.ExcCount; i < iMax; i++) { - if (_pools[_componentIDToPoolID[bakedMask.Exc[i]]].Has(entity)) + if (_pools[bakedMask.Exc[i]].Has(entity)) { return false; } @@ -137,7 +143,7 @@ namespace DCFApixels.DragonECS BakedMask bakedMask = mask.GetBaked(); for (int i = 0, iMax = bakedMask.IncCount; i < iMax; i++) { - int poolID = _componentIDToPoolID[bakedMask.Inc[i]]; + int poolID = bakedMask.Inc[i]; if (poolID == otherPoolID || !_pools[poolID].Has(entity)) { return false; @@ -145,7 +151,7 @@ namespace DCFApixels.DragonECS } for (int i = 0, iMax = bakedMask.ExcCount; i < iMax; i++) { - int poolID = _componentIDToPoolID[bakedMask.Exc[i]]; + int poolID = bakedMask.Exc[i]; if (poolID != otherPoolID && _pools[poolID].Has(entity)) { return false; @@ -212,20 +218,18 @@ namespace DCFApixels.DragonECS #endregion #region NewEntity - public Entity NewEntity() + public ent NewEntity() { - int entityID = _entities.GetFree(); - _entities.Normalize(ref _gens); - _gens[entityID]++; - - return new Entity(this, entityID); + int entid = _entityDispenser.GetFree(); + if(_gens.Length < entid) Array.Resize(ref _gens, _gens.Length << 1); + return new ent(entid, _gens[entid]++, id); } #endregion #region Destroy public void Destroy() { - _id = IEcsWorld.DEAD_WORLD_ID; + } #endregion diff --git a/src/IEcsWorld.cs.meta b/src/EcsWorld.cs.meta similarity index 100% rename from src/IEcsWorld.cs.meta rename to src/EcsWorld.cs.meta diff --git a/src/INFO.txt b/src/INFO.txt index a6001cd..621e141 100644 --- a/src/INFO.txt +++ b/src/INFO.txt @@ -1,3 +1,6 @@ Мервтый мир - значение byte 255, зарезервированный адишник мира, все что ссылается на мертвый мир считается так же мертвым. -DCFAECS_NO_SANITIZE_CHECKS - отвключение дополнительных проверок \ No newline at end of file +DCFAECS_NO_SANITIZE_CHECKS - отвключение дополнительных проверок + + public const int MAX_WORLDS = byte.MaxValue; //Номер последнего мира 254 + public const int DEAD_WORLD_ID = byte.MaxValue; //Зарезервированный номер мира для мертвых сущьностей \ No newline at end of file diff --git a/src/Interfaces/IEcsProcessor.cs b/src/Interfaces/IEcsProcessor.cs index f9bdaa6..3d3f9de 100644 --- a/src/Interfaces/IEcsProcessor.cs +++ b/src/Interfaces/IEcsProcessor.cs @@ -2,14 +2,12 @@ { public interface IEcsProcessor { } - public interface IEcsDoTag { } - public struct _PreInit : IEcsDoTag { } - public struct _Init : IEcsDoTag { } - public struct _Run : IEcsDoTag { } - public struct _Destroy : IEcsDoTag { } - public struct _PostDestroy : IEcsDoTag { } + public struct _PreInit { } + public struct _Init { } + public struct _Run { } + public struct _Destroy { } + public struct _PostDestroy { } public interface IDo : IEcsProcessor - where TTag : IEcsDoTag { public void Do(EcsSession session); } @@ -53,19 +51,4 @@ { public void Do(EcsSession session, in TMessage m); } - - - public struct _OnComponentRemoved : IEcsMessage - { - public int entityID; - } - public struct _OnComponentAdded : IEcsMessage - { - public int entityID; - } - public interface IEcsGReceive : IEcsProcessor - where TMessage : IEcsMessage - { - public void Do(EcsSession session, in TMessage m, in T obj); - } } diff --git a/src/Primitives/Entity.cs b/src/Primitives/Entity.cs index f3d61d0..6b169c2 100644 --- a/src/Primitives/Entity.cs +++ b/src/Primitives/Entity.cs @@ -31,17 +31,17 @@ namespace DCFApixels.DragonECS } // но чтобы значене default было NULL сульностью, мир хранится в виде ID + 1 [EditorBrowsable(EditorBrowsableState.Never)] - public ushort world + public short world { [MethodImpl(MethodImplOptions.AggressiveInlining)] - get => (ushort)(((_full << 48) >> 48) - 1); + get => (short)(((_full << 48) >> 48) - 1); } #endregion #region Constructors [EditorBrowsable(EditorBrowsableState.Never)] - public ent(int id, short gen, ushort world) + public ent(int id, short gen, short world) { _full = ((long)id) << 32; _full += ((long)gen) << 16; @@ -104,22 +104,83 @@ namespace DCFApixels.DragonECS #endregion } - public static class entExtensions + public static partial class entExtensions { + [MethodImpl(MethodImplOptions.AggressiveInlining)] public static bool IsNull(this in ent self) { return self == ent.NULL; } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static ref readonly T Read(this in ent self) + where T : struct + { + 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 + { + return ref EcsWorld.Worlds[self.world].GetPool().Write(self.id); + } + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static bool Has(this in ent self) + where T : struct + { + return EcsWorld.Worlds[self.world].GetPool().Has(self.id); + } + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static void Del(this in ent self) + where T : struct + { + EcsWorld.Worlds[self.world].GetPool().Del(self.id); + } } - public readonly ref struct Entity + public struct Entity { - internal readonly IEcsWorld world; - internal readonly int id; - public Entity(IEcsWorld world, ent id) + public IEcsWorld world; + public int id; + + public Entity(IEcsWorld world, int id) { this.world = world; this.id = id; } } + + public static partial class EntityExtensions + { + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static bool IsNull(this in Entity self) + { + return self.world == null; + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static ref readonly T Read(this in Entity self) + where T : struct + { + return ref self.world.GetPool().Read(self.id); + } + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static ref T Write(this in Entity self) + where T : struct + { + return ref self.world.GetPool().Write(self.id); + } + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static bool Has(this in Entity self) + where T : struct + { + return self.world.GetPool().Has(self.id); + } + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static void Del(this in Entity self) + where T : struct + { + self.world.GetPool().Del(self.id); + } + } } diff --git a/src/React/EcsProcessorsGMessenger.cs b/src/React/EcsProcessorsGMessenger.cs deleted file mode 100644 index 0eb9c01..0000000 --- a/src/React/EcsProcessorsGMessenger.cs +++ /dev/null @@ -1,41 +0,0 @@ -using System; -using System.Collections.Generic; - -namespace DCFApixels.DragonECS -{ - public class EcsProcessorsGMessenger : IEcsProcessorsMessenger, IDisposable - where TMessage : IEcsMessage - { - private readonly EcsSession _source; - private readonly IEcsGReceive[] _targets; - - public EcsSession Source => _source; - public IReadOnlyList> Targets => _targets; - - internal EcsProcessorsGMessenger(EcsSession source) - { - _source = source; - List> list = new List>(); - - foreach (var item in _source.AllProcessors) - { - if (item is IEcsGReceive targetItem) - { - list.Add(targetItem); - } - } - _targets = list.ToArray(); - } - - public void Send(in TMessage message, in T obj) - { - foreach (var item in _targets) - { - item.Do(_source, in message, in obj); - } - } - - public void Destroy() => _source.OnMessengerDetroyed(this); - void IDisposable.Dispose() => Destroy(); - } -} diff --git a/src/React/EcsProcessorsMessenger.cs b/src/React/EcsProcessorsMessenger.cs index 53ebc38..8357ff5 100644 --- a/src/React/EcsProcessorsMessenger.cs +++ b/src/React/EcsProcessorsMessenger.cs @@ -32,6 +32,10 @@ namespace DCFApixels.DragonECS _targets = list.ToArray(); } + public void Send(TMessage message) + { + Send(in message); + } public void Send(in TMessage message) { foreach (var item in _targets) diff --git a/src/React/EcsProcessorsRunner.cs b/src/React/EcsProcessorsRunner.cs index d5e9c1a..17a87a3 100644 --- a/src/React/EcsProcessorsRunner.cs +++ b/src/React/EcsProcessorsRunner.cs @@ -9,7 +9,6 @@ namespace DCFApixels.DragonECS public void Run(); } public class EcsProcessorsRunner : IEcsProcessorsRunner, IDisposable - where TDoTag : IEcsDoTag { private readonly EcsSession _source; private readonly IDo[] _targets; diff --git a/src/React/RunnerHandler.cs b/src/React/RunnerHandler.cs new file mode 100644 index 0000000..85776e3 --- /dev/null +++ b/src/React/RunnerHandler.cs @@ -0,0 +1,126 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Reflection; +using System.Runtime.CompilerServices; + +namespace DCFApixels.DragonECS +{ + [AttributeUsage(AttributeTargets.Class, Inherited = false, AllowMultiple = true)] + sealed class RunnerFilterAttribute : Attribute + { + public readonly Type interfaceType; + public readonly object filter; + public RunnerFilterAttribute(Type interfaceType, object filter) + { + this.interfaceType = interfaceType; + this.filter = filter; + } + } + + public interface IProcessor { } + + public static class IProcessorExtensions + { + public static bool IsRunner(this IProcessor self) + { + return self is IRunner; + } + } + + internal static class RunnerActivator + { + private static bool _isInit = false; + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static void Init() + { + if (_isInit) return; + Type targetType = typeof(Runner<>); + var subclasses = Assembly.GetAssembly(targetType).GetTypes().Where(type => type.BaseType != null && type.BaseType.IsGenericType && targetType == type.BaseType.GetGenericTypeDefinition()); + foreach (var item in subclasses) + { + item.BaseType.GetMethod("Init", BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.Static).Invoke(null, new object[] { item }); + } + _isInit = true; + } + } + + public interface IRunner { } + + public abstract class Runner : IProcessor, IRunner + where TInterface : IProcessor + { + internal static void Init(Type subclass) + { + +#if DEBUG || DCFAECS_NO_SANITIZE_CHECKS + if (_subclass != null) + { + throw new ArgumentException($"The Runner<{typeof(TInterface).FullName}> can only have one subclass"); + } + + Type interfaceType = typeof(TInterface); + + var interfaces = interfaceType.GetInterfaces(); + if (interfaceType.IsInterface == false) + { + throw new ArgumentException($"{typeof(TInterface).FullName} is not interface"); + } + if (interfaces.Length != 1 || interfaces[0] != typeof(IProcessor)) + { + throw new ArgumentException($"{typeof(TInterface).FullName} does not directly inherit the {nameof(IProcessor)} interface"); + } +#endif + _subclass = subclass; + } + + public static TInterface Instantiate(IEnumerable targets, object filter) + { + Type interfaceType = typeof(TInterface); + + IEnumerable newTargets; + + if (filter != null) + { + newTargets = targets.Where(o => + { + if (o is TInterface == false) return false; + var atr = o.GetType().GetCustomAttribute(); + return atr != null && atr.interfaceType == interfaceType && atr.filter.Equals(filter); + }); + } + else + { + newTargets = targets.Where(o => + { + if (o is TInterface == false) return false; + var atr = o.GetType().GetCustomAttribute(); + return atr == null || atr.interfaceType == interfaceType && atr.filter == null; + }); + } + + return Instantiate(newTargets.Select(o => (TInterface)o).ToArray()); + } + public static TInterface Instantiate(IEnumerable targets) + { + return Instantiate(targets.Where(o => o is TInterface).Select(o => (TInterface)o).ToArray()); + } + internal static TInterface Instantiate(TInterface[] targets) + { + RunnerActivator.Init(); + var instance = (Runner)Activator.CreateInstance(_subclass); + return (TInterface)(IProcessor)instance.Set(targets); + } + + + + private static Type _subclass; + protected TInterface[] targets; + + private Runner Set(TInterface[] targets) + { + this.targets = targets; + return this; + } + } +} diff --git a/src/React/EcsProcessorsGMessenger.cs.meta b/src/React/RunnerHandler.cs.meta similarity index 83% rename from src/React/EcsProcessorsGMessenger.cs.meta rename to src/React/RunnerHandler.cs.meta index 8447895..e1ae744 100644 --- a/src/React/EcsProcessorsGMessenger.cs.meta +++ b/src/React/RunnerHandler.cs.meta @@ -1,5 +1,5 @@ fileFormatVersion: 2 -guid: d340db685521a624792157f350923088 +guid: ae6eb3472cd282b46b26ab9f1e97ec81 MonoImporter: externalObjects: {} serializedVersion: 2 diff --git a/src/Utils/IntDispenser.cs b/src/Utils/IntDispenser.cs new file mode 100644 index 0000000..4718ec3 --- /dev/null +++ b/src/Utils/IntDispenser.cs @@ -0,0 +1,44 @@ +using System.Collections.Concurrent; +using System.Threading; + +namespace DCFApixels.DragonECS +{ + internal sealed class IntDispenser + { + private readonly ConcurrentStack _freeInts; + private int _increment; + + #region Properties + public int LastInt => _increment; + #endregion + + #region Constructor + public IntDispenser() + { + _freeInts = new ConcurrentStack(); + _increment = 0; + } + public IntDispenser(int startIncrement) + { + _freeInts = new ConcurrentStack(); + _increment = startIncrement; + } + #endregion + + #region GetFree/Release + public int GetFree() + { + if (!_freeInts.TryPop(out int result)) + { + result = Interlocked.Increment(ref _increment); + } + return result; + } + + public void Release(int released) + { + _freeInts.Push(released); + } + #endregion + } +} diff --git a/src/Builtin/FiltersProcessor.cs.meta b/src/Utils/IntDispenser.cs.meta similarity index 83% rename from src/Builtin/FiltersProcessor.cs.meta rename to src/Utils/IntDispenser.cs.meta index 5f39fc2..b28c4e4 100644 --- a/src/Builtin/FiltersProcessor.cs.meta +++ b/src/Utils/IntDispenser.cs.meta @@ -1,5 +1,5 @@ fileFormatVersion: 2 -guid: db219c3a9acc0964f8808af51e32afc3 +guid: a20cbd833c35ff3438f9fe003903c0c3 MonoImporter: externalObjects: {} serializedVersion: 2 diff --git a/src/Utils/SparseSet.cs b/src/Utils/SparseSet.cs index e8dec51..233c2a3 100644 --- a/src/Utils/SparseSet.cs +++ b/src/Utils/SparseSet.cs @@ -3,115 +3,124 @@ using System.Collections; using System.Collections.Generic; using System.Runtime.CompilerServices; using System.Text; -using coretype = System.Int32; namespace DCFApixels.DragonECS { - public class SparseSet : IEnumerable, ICollection, IReadOnlyCollection + public class SparseSet : IEnumerable, ICollection, IReadOnlyCollection { - public const int DEFAULT_CAPACITY = 16; - public const int MAX_CAPACITY = coretype.MaxValue; + public const int DEFAULT_DENSE_CAPACITY = 8; + public const int DEFAULT_SPARSE_CAPACITY = 16; - private coretype[] _dense; - private coretype[] _sparse; + public const int MIN_CAPACITY = 4; - private coretype _count; + public const int MAX_CAPACITY = int.MaxValue; - private coretype _denseCapacity; + private int[] _dense; + private int[] _sparse; + + private int _count; #region Properties - public int Count => _count; - public int CapacityDense + public int Count { [MethodImpl(MethodImplOptions.AggressiveInlining)] - get => _denseCapacity; + get => _count; } - public int CapacitySparse + public int CapacityDense { [MethodImpl(MethodImplOptions.AggressiveInlining)] get => _dense.Length; } - - public coretype this[int index] + public int CapacitySparse + { + [MethodImpl(MethodImplOptions.AggressiveInlining)] + get => _sparse.Length; + } + + public int this[int index] { [MethodImpl(MethodImplOptions.AggressiveInlining)] -#if DEBUG get { - ThrowHalper.CheckOutOfRange(this, (coretype)index); +#if DEBUG + ThrowHalper.CheckOutOfRange(this, index); +#endif return _dense[index]; } -#else - get => _dense[index]; -#endif } #endregion #region Constructors - public SparseSet() : this(DEFAULT_CAPACITY) { } - public SparseSet(coretype capacity) + public SparseSet() : this(DEFAULT_DENSE_CAPACITY, DEFAULT_SPARSE_CAPACITY) { } + public SparseSet(int denseCapacity, int sparseCapacity) { -#if DEBUG - ThrowHalper.CheckCapacity(capacity); -#endif - _dense = new coretype[capacity]; - _sparse = new coretype[capacity]; - for (coretype i = 0; i < _sparse.Length; i++) - { - _dense[i] = i; - _sparse[i] = i; - } - _count = 0; - _denseCapacity = 0; + denseCapacity = denseCapacity < MIN_CAPACITY ? MIN_CAPACITY : NormalizeCapacity(denseCapacity); + sparseCapacity = sparseCapacity < MIN_CAPACITY ? MIN_CAPACITY : NormalizeCapacity(sparseCapacity); + + _dense = new int[denseCapacity]; + _sparse = new int[sparseCapacity]; + + Reset(); } #endregion - #region Add/AddRange/GetFree - public void Add(coretype value, ref T[] normalizedArray) + #region Add/AddRange + public void Add(int value, ref T[] normalizedArray) { Add(value); Normalize(ref normalizedArray); } - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public void Add(coretype value) + public void Add(int value) { #if DEBUG ThrowHalper.CheckValueIsPositive(value); ThrowHalper.CheckValueNotContained(this, value); #endif - if (value > CapacitySparse) + if (_count >= _dense.Length) + Array.Resize(ref _dense, _dense.Length << 1); + + if (value > _sparse.Length) { - coretype neadedSpace = (coretype)_dense.Length; - while (value >= neadedSpace) neadedSpace <<= 1; - Resize(neadedSpace); + int neadedSpace = _sparse.Length; + while (value >= neadedSpace) + neadedSpace <<= 1; + int i = _sparse.Length; + Array.Resize(ref _sparse, neadedSpace); + //loop unwinding + for (; i < neadedSpace;) + { + _sparse[i++] = -1; + _sparse[i++] = -1; + _sparse[i++] = -1; + _sparse[i++] = -1; + } } - Swap(value, _count++); - if (_count > _denseCapacity) _denseCapacity <<= 1; + _dense[_count] = value; + _sparse[value] = _count++; } - public bool TryAdd(coretype value, ref T[] normalizedArray) + public bool TryAdd(int value, ref T[] normalizedArray) { if (Contains(value)) return false; Add(value); Normalize(ref normalizedArray); return true; } - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public bool TryAdd(coretype value) + public bool TryAdd(int value) { if (Contains(value)) return false; Add(value); return true; } - public void AddRange(IEnumerable range, ref T[] normalizedArray) + public void AddRange(IEnumerable range, ref T[] normalizedArray) { AddRange(range); Normalize(ref normalizedArray); } - public void AddRange(IEnumerable range) + public void AddRange(IEnumerable range) { foreach (var item in range) { @@ -119,46 +128,28 @@ namespace DCFApixels.DragonECS Add(item); } } - - /// Adds a value between 0 and Capacity to the array and returns it. - /// Value between 0 and Capacity - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public coretype GetFree(ref T[] normalizedArray) - { - coretype result = GetFree(); - Normalize(ref normalizedArray); - return result; - } - /// Adds a value between 0 and Capacity to the array and returns it. - /// Value between 0 and Capacity - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public coretype GetFree() - { - if (++_count >= CapacitySparse) AddSpaces(); - if (_count > _denseCapacity) _denseCapacity <<= 1; - return _dense[_count - 1]; - } #endregion #region Contains [MethodImpl(MethodImplOptions.AggressiveInlining)] - public bool Contains(coretype value) + public bool Contains(int value) { - return value >= 0 && value < CapacitySparse && _sparse[value] < _count; + return value >= 0 && value < CapacitySparse && _sparse[value] >= 0; } #endregion #region Remove - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public void Remove(coretype value) + public void Remove(int value) { #if DEBUG ThrowHalper.CheckValueContained(this, value); #endif - Swap(_sparse[value], --_count); + _dense[_sparse[value]] = _dense[--_count]; + _sparse[_dense[_count]] = _sparse[value]; + _sparse[value] = -1; } [MethodImpl(MethodImplOptions.AggressiveInlining)] - public bool TryRemove(coretype value) + public bool TryRemove(int value) { if (!Contains(value)) return false; Remove(value); @@ -166,7 +157,7 @@ namespace DCFApixels.DragonECS } [MethodImpl(MethodImplOptions.AggressiveInlining)] - public void RemoveAt(coretype index) + public void RemoveAt(int index) { #if DEBUG ThrowHalper.CheckOutOfRange(this, index); @@ -183,7 +174,7 @@ namespace DCFApixels.DragonECS } [MethodImpl(MethodImplOptions.AggressiveInlining)] - public int IndexOf(coretype value) + public int IndexOf(int value) { if (value < 0 || !Contains(value)) return -1; return _sparse[value]; @@ -191,8 +182,8 @@ namespace DCFApixels.DragonECS public void Sort() { - coretype increment = 0; - for (coretype i = 0; i < CapacitySparse; i++) + int increment = 0; + for (int i = 0; i < CapacitySparse; i++) { if (_sparse[i] < _count) { @@ -204,18 +195,17 @@ namespace DCFApixels.DragonECS public void HardSort() { - coretype inc = 0; - coretype inc2 = _count; - for (coretype i = 0; i < CapacitySparse; i++) + int inc = 0; + int inc2 = _count; + for (int i = 0; i < CapacitySparse; i++) { - if (_sparse[i] < _count) + if (_sparse[i] >= 0) { _sparse[i] = inc; _dense[inc++] = i; } else { - _sparse[i] = inc2; _dense[inc2++] = i; } } @@ -225,14 +215,14 @@ namespace DCFApixels.DragonECS { other._count = _count; if (CapacitySparse != other.CapacitySparse) - { - other.Resize(CapacitySparse); - } - _dense.CopyTo(other._dense, 0); + Array.Resize(ref other._sparse, CapacitySparse); + if (CapacityDense != other.CapacityDense) + Array.Resize(ref other._dense, CapacityDense); _sparse.CopyTo(other._sparse, 0); + _dense.CopyTo(other._dense, 0); } - public void CopyTo(coretype[] array, int arrayIndex) + public void CopyTo(int[] array, int arrayIndex) { #if DEBUG if (arrayIndex < 0) throw new ArgumentException("arrayIndex is less than 0"); @@ -243,6 +233,12 @@ namespace DCFApixels.DragonECS array[arrayIndex] = this[i]; } } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + private int NormalizeCapacity(int value) + { + return value + (MIN_CAPACITY - (value % MIN_CAPACITY)); + } #endregion #region Clear/Reset @@ -251,52 +247,26 @@ namespace DCFApixels.DragonECS public void Reset() { Clear(); - for (coretype i = 0; i < _dense.Length; i++) + //loop unwinding + for (int i = 0; i < _sparse.Length;) { - _dense[i] = i; - _sparse[i] = i; + _sparse[i++] = -1; + _sparse[i++] = -1; + _sparse[i++] = -1; + _sparse[i++] = -1; } } - public void Reset(coretype newCapacity) + + public void Reset(int newDenseCapacity, int newSparseCapacity) { -#if DEBUG - ThrowHalper.CheckCapacity(newCapacity); -#endif + newDenseCapacity = newDenseCapacity < MIN_CAPACITY ? MIN_CAPACITY : NormalizeCapacity(newDenseCapacity); + newSparseCapacity = newSparseCapacity < MIN_CAPACITY ? MIN_CAPACITY : NormalizeCapacity(newSparseCapacity); + + if (CapacitySparse != newSparseCapacity) + Array.Resize(ref _sparse, newSparseCapacity); + if (CapacityDense != newDenseCapacity) + Array.Resize(ref _dense, newDenseCapacity); Reset(); - Resize(newCapacity); - } - #endregion - - #region AddSpace/Resize - [MethodImpl(MethodImplOptions.AggressiveInlining)] - private void AddSpaces() => Resize((_count << 1)); - - [MethodImpl(MethodImplOptions.AggressiveInlining)] - private void Resize(int newSpace) - { - coretype oldspace = (short)_dense.Length; - Array.Resize(ref _dense, newSpace); - Array.Resize(ref _sparse, newSpace); - - for (coretype i = oldspace; i < newSpace; i++) - { - _dense[i] = i; - _sparse[i] = i; - } - } - #endregion - - #region Swap - [MethodImpl(MethodImplOptions.AggressiveInlining)] - private void Swap(coretype fromIndex, coretype toIndex) - { - coretype value = _dense[toIndex]; - coretype oldValue = _dense[fromIndex]; - - _dense[toIndex] = oldValue; - _dense[fromIndex] = value; - _sparse[_dense[fromIndex]] = fromIndex; - _sparse[_dense[toIndex]] = toIndex; } #endregion @@ -306,19 +276,19 @@ namespace DCFApixels.DragonECS public ref struct RefEnumerator { - private readonly coretype[] _dense; - private readonly coretype _count; - private coretype _index; + private readonly int[] _dense; + private readonly int _count; + private int _index; [MethodImpl(MethodImplOptions.AggressiveInlining)] - public RefEnumerator(coretype[] values, coretype count) + public RefEnumerator(int[] values, int count) { _dense = values; _count = count; _index = -1; } - public coretype Current + public int Current { [MethodImpl(MethodImplOptions.AggressiveInlining)] get => _dense[_index]; @@ -334,20 +304,20 @@ namespace DCFApixels.DragonECS public void Reset() => _index = -1; } - IEnumerator IEnumerable.GetEnumerator() => new Enumerator(_dense, _count); + IEnumerator IEnumerable.GetEnumerator() => new Enumerator(_dense, _count); IEnumerator IEnumerable.GetEnumerator() => new Enumerator(_dense, _count); - public struct Enumerator : IEnumerator //to implement the IEnumerable interface and use the ref structure, 2 Enumerators were created. + public struct Enumerator : IEnumerator //to implement the IEnumerable interface and use the ref structure, 2 Enumerators were created. { - private readonly coretype[] _dense; - private readonly coretype _count; - private coretype _index; - public Enumerator(coretype[] values, coretype count) + private readonly int[] _dense; + private readonly int _count; + private int _index; + public Enumerator(int[] values, int count) { _dense = values; _count = count; _index = -1; } - public coretype Current => _dense[_index]; + public int Current => _dense[_index]; object IEnumerator.Current => _dense[_index]; public void Dispose() { } public bool MoveNext() => ++_index < _count; @@ -356,16 +326,16 @@ namespace DCFApixels.DragonECS #endregion #region ICollection - bool ICollection.IsReadOnly => false; + bool ICollection.IsReadOnly => false; - bool ICollection.Remove(coretype value) => TryRemove(value); + bool ICollection.Remove(int value) => TryRemove(value); #endregion #region Debug public string Log() { StringBuilder logbuild = new StringBuilder(); - for (int i = 0; i < CapacitySparse; i++) + for (int i = 0; i < CapacityDense; i++) { logbuild.Append(_dense[i] + ", "); } @@ -376,14 +346,14 @@ namespace DCFApixels.DragonECS } logbuild.Append("\n\r --------------------------"); logbuild.Append("\n\r"); - for (int i = 0; i < CapacitySparse; i++) + for (int i = 0; i < CapacityDense; i++) { logbuild.Append((i < _count ? _dense[i].ToString() : "_") + ", "); } logbuild.Append("\n\r"); for (int i = 0; i < CapacitySparse; i++) { - logbuild.Append((_sparse[i] < _count ? _sparse[i].ToString() : "_") + ", "); + logbuild.Append((_sparse[i] >= 0 ? _sparse[i].ToString() : "_") + ", "); } logbuild.Append("\n\r Count: " + _count); logbuild.Append("\n\r Capacity: " + CapacitySparse); @@ -396,7 +366,7 @@ namespace DCFApixels.DragonECS public bool IsValide_Debug() { bool isPass = true; - for (int index = 0; index < CapacitySparse; index++) + for (int index = 0; index < _count; index++) { int value = _dense[index]; isPass = isPass && _sparse[value] == index; @@ -407,27 +377,27 @@ namespace DCFApixels.DragonECS #if DEBUG private static class ThrowHalper { - public static void CheckCapacity(coretype capacity) + public static void CheckCapacity(int capacity) { if (capacity < 0) throw new ArgumentException("Capacity cannot be a negative number"); } - public static void CheckValueIsPositive(coretype value) + public static void CheckValueIsPositive(int value) { if (value < 0) throw new ArgumentException("The SparseSet can only contain positive numbers"); } - public static void CheckValueContained(SparseSet source, coretype value) + public static void CheckValueContained(SparseSet source, int value) { if (!source.Contains(value)) throw new ArgumentException($"Value {value} is not contained"); } - public static void CheckValueNotContained(SparseSet source, coretype value) + public static void CheckValueNotContained(SparseSet source, int value) { if (source.Contains(value)) throw new ArgumentException($"Value {value} is already contained"); } - public static void CheckOutOfRange(SparseSet source, coretype index) + public static void CheckOutOfRange(SparseSet source, int index) { if (index < 0 || index >= source.Count) throw new ArgumentOutOfRangeException($"Index {index} was out of range. Must be non-negative and less than the size of the collection."); diff --git a/test/Startup.cs b/test/Startup.cs index 0944e82..50fa5e2 100644 --- a/test/Startup.cs +++ b/test/Startup.cs @@ -14,7 +14,6 @@ namespace DCFApixels.DragonECS private void Start() { _ecsSession - .AddWorld("") .Add(new TestSystem()) .Inject(_data) .Init();