diff --git a/src/Builtin/Components.cs b/src/Builtin/Components.cs new file mode 100644 index 0000000..1c9d67c --- /dev/null +++ b/src/Builtin/Components.cs @@ -0,0 +1,30 @@ +using System.Runtime.CompilerServices; +using System.Runtime.InteropServices; + +namespace DCFApixels.DragonECS +{ + /// + /// Используется для реализации отношений. traget - это сущьность к которой крепится эта сущьность. other - это сущьность с которой traget образует связь + /// + [StructLayout(LayoutKind.Explicit, Pack = 8, Size = 16)] + public readonly struct Attach + { + [FieldOffset(0), MarshalAs(UnmanagedType.U8)] + public readonly EcsEntity target; + [FieldOffset(1), MarshalAs(UnmanagedType.U8)] + public readonly EcsEntity other; + + /// alias for "target" + public EcsEntity left + { + [MethodImpl(MethodImplOptions.AggressiveInlining)] + get => target; + } + /// alias for "other" + public EcsEntity right + { + [MethodImpl(MethodImplOptions.AggressiveInlining)] + get => other; + } + } +} diff --git a/src/EcsGroup.cs b/src/EcsGroup.cs index ee0a76e..1097010 100644 --- a/src/EcsGroup.cs +++ b/src/EcsGroup.cs @@ -2,6 +2,7 @@ using System.Runtime.CompilerServices; using System.Runtime.InteropServices; using Unity.Profiling; +using UnityEngine; using delayedOp = System.Int32; namespace DCFApixels.DragonECS @@ -10,7 +11,6 @@ namespace DCFApixels.DragonECS public readonly ref struct EcsReadonlyGroup { private readonly EcsGroup _source; - #region Constructors [MethodImpl(MethodImplOptions.AggressiveInlining)] public EcsReadonlyGroup(EcsGroup source) => _source = source; @@ -49,11 +49,8 @@ namespace DCFApixels.DragonECS public bool Contains(int entityID) => _source.Contains(entityID); [MethodImpl(MethodImplOptions.AggressiveInlining)] public EcsGroup.Enumerator GetEnumerator() => _source.GetEnumerator(); - - /// Equivalent of the EcsGroup.Clone() method - /// An editable clone of this EcsReadnolyGroup [MethodImpl(MethodImplOptions.AggressiveInlining)] - public EcsGroup Extract() => _source.Clone(); + public EcsGroup Clone() => _source.Clone(); #endregion #region Object @@ -80,23 +77,10 @@ namespace DCFApixels.DragonECS public static bool operator !=(EcsReadonlyGroup a, EcsReadonlyGroup b) => !a.Equals(b); public static bool operator !=(EcsReadonlyGroup a, EcsGroup b) => !a.Equals(b); #endregion - - #region Dispose/Release - public void Dispose() - { - _source.Dispose(); - } - public void Release() - { - _source.World.ReleaseGroup(_source); - } - #endregion } - // не может содержать значение 0 + // индексация начинается с 1 // _delayedOps это int[] для отложенных операций, хранятся отложенные операции в виде int значения, если старший бит = 0 то это опреация добавленияб если = 1 то это операция вычитания - - // this collection can only store numbers greater than 0 public unsafe class EcsGroup : IDisposable, IEquatable { private const int DEALAYED_ADD = 0; @@ -145,7 +129,8 @@ namespace DCFApixels.DragonECS } #endregion - #region Constrcutors + #region Constrcutors/Finalizer + [MethodImpl(MethodImplOptions.AggressiveInlining)] public static EcsGroup New(IEcsWorld world) { return world.GetGroupFromPool(); @@ -164,6 +149,14 @@ namespace DCFApixels.DragonECS _delayedOpsCount = 0; _count = 0; } + + //защита от криворукости + //перед сборкой мусора снова создает сильную ссылку и возвращает в пул + //TODO переделат ьиил удалить, так как сборщик мусора просыпается только после 12к и более экземпляров, только тогда и вызывается финализатор, слишком жирно + ~EcsGroup() + { + Release(); + } #endregion #region Contains @@ -256,12 +249,10 @@ namespace DCFApixels.DragonECS } } } - public void Clear() { _count = 0; - for (int i = 0; i < _dense.Length; i++) - _dense[i] = 0; + //массив _dense нет смысла очищать, испольщуется только область от 1 до _count for (int i = 0; i < _sparse.Length; i++) _sparse[i] = 0; } @@ -275,7 +266,8 @@ namespace DCFApixels.DragonECS #if (DEBUG && !DISABLE_DEBUG) || !DRAGONECS_NO_SANITIZE_CHECKS if (group.World != _source) throw new ArgumentException("groupFilter.World != World"); #endif - Clear(); + if(_count > 0) + Clear(); foreach (var item in group) AggressiveAdd(item.id); } @@ -428,14 +420,14 @@ namespace DCFApixels.DragonECS #region Enumerator public ref struct Enumerator// : IDisposable { - // private readonly EcsGroup _source; + // private readonly EcsGroup source; private readonly int[] _dense; private readonly int _count; private int _index; [MethodImpl(MethodImplOptions.AggressiveInlining)] public Enumerator(EcsGroup group) { - // _source = group; + // source = group; _dense = group._dense; _count = group.Count; _index = 0; @@ -448,7 +440,7 @@ namespace DCFApixels.DragonECS [MethodImpl(MethodImplOptions.AggressiveInlining)] public bool MoveNext() => ++_index <= _count && _count<_dense.Length; // <= потму что отсчет начинается с индекса 1 //_count < _dense.Length дает среде понять что проверки на выход за границы не нужны //[MethodImpl(MethodImplOptions.AggressiveInlining)] - //public void Dispose() => _source.Unlock(); + //public void Dispose() => source.Unlock(); } #endregion diff --git a/src/EcsMask.cs b/src/EcsMask.cs index 543821a..5de79e2 100644 --- a/src/EcsMask.cs +++ b/src/EcsMask.cs @@ -357,27 +357,27 @@ namespace DCFApixels.DragonECS { static Activator() { - // var inc = new TInc().GetComponentsIDs(); - // var exc = new TExc().GetComponentsIDs(); - // Array.Sort(inc); - // Array.Sort(exc); + // var inc_ = new TInc().GetComponentsIDs(); + // var exc_ = new TExc().GetComponentsIDs(); + // Array.Sort(inc_); + // Array.Sort(exc_); // // Type thisType = typeof(Activator); // // Type sortedIncType = typeof(TInc); // if (sortedIncType.IsGenericType) // { - // Type[] sortedInc = new Type[inc.Length]; + // Type[] sortedInc = new Type[inc_.Length]; // for (int i = 0; i < sortedInc.Length; i++) - // sortedInc[i] = EcsWorld.ComponentType.types[inc[i]]; + // sortedInc[i] = EcsWorld.ComponentType.types[inc_[i]]; // sortedIncType = sortedIncType.GetGenericTypeDefinition().MakeGenericType(sortedInc); // } // Type sortedExcType = typeof(TExc); // if (sortedExcType.IsGenericType) // { - // Type[] sortedExc = new Type[exc.Length]; + // Type[] sortedExc = new Type[exc_.Length]; // for (int i = 0; i < sortedExc.Length; i++) - // sortedExc[i] = EcsWorld.ComponentType.types[exc[i]]; + // sortedExc[i] = EcsWorld.ComponentType.types[exc_[i]]; // sortedExcType = sortedExcType.GetGenericTypeDefinition().MakeGenericType(sortedExc); // } // @@ -393,7 +393,7 @@ namespace DCFApixels.DragonECS // if (_count >= _capacity) // _capacity <<= 1; // - // instance = new EcsMask(typeof(TWorldArchetype), id, inc, exc); + // instance = new EcsMask(typeof(TWorldArchetype), id, inc_, exc_); } public readonly static EcsMask instance; diff --git a/src/EcsPool.cs b/src/EcsPool.cs index 682e784..e46d52a 100644 --- a/src/EcsPool.cs +++ b/src/EcsPool.cs @@ -1,21 +1,25 @@ using System; using System.Runtime.CompilerServices; using Unity.Profiling; -using UnityEngine; namespace DCFApixels.DragonECS { public interface IEcsPool { + #region Properties public Type ComponentType { get; } public int ComponentID { get; } public IEcsWorld World { get; } public int Count { get; } public int Capacity { get; } + #endregion + + #region Methods public bool Has(int entityID); public void Write(int entityID); public void Del(int entityID); internal void OnWorldResize(int newSize); + #endregion } public interface IEcsPool : IEcsPool where T : struct { @@ -28,13 +32,18 @@ namespace DCFApixels.DragonECS { public static EcsNullPool instance => new EcsNullPool(null); private IEcsWorld _source; - private EcsNullPool(IEcsWorld source) => _source = source; private NullComponent fakeComponent; + private EcsNullPool(IEcsWorld source) => _source = source; + + #region Properties public Type ComponentType => typeof(NullComponent); public int ComponentID => -1; public IEcsWorld World => _source; public int Count => 0; public int Capacity => 1; + #endregion + + #region Methods public void Del(int index) { } public override bool Has(int index) => false; void IEcsPool.Write(int entityID) { } @@ -42,6 +51,7 @@ namespace DCFApixels.DragonECS public ref NullComponent Write(int entity) => ref fakeComponent; void IEcsPool.OnWorldResize(int newSize) { } internal override void OnWorldResize(int newSize) { } + #endregion } public abstract class EcsPool { @@ -62,6 +72,7 @@ namespace DCFApixels.DragonECS private IEcsComponentReset _componentResetHandler; private PoolRunnres _poolRunnres; + #region Properites public int Count => _itemsCount; public int Capacity => _items.Length; @@ -115,11 +126,12 @@ namespace DCFApixels.DragonECS _mapping[entityID] = itemIndex; _componentResetHandler.Reset(ref _items[itemIndex]); _poolRunnres.add.OnComponentAdd(entityID); - } + } _poolRunnres.write.OnComponentWrite(entityID); return ref _items[itemIndex]; // } } + [MethodImpl(MethodImplOptions.AggressiveInlining)] public ref T Write(int entityID) { // using (_writeMark.Auto()) @@ -134,7 +146,7 @@ namespace DCFApixels.DragonECS [MethodImpl(MethodImplOptions.AggressiveInlining)] public sealed override bool Has(int entityID) { - // using (_hasMark.Auto()) + // using (_hasMark.Auto()) return _mapping[entityID] > 0; } public void Del(int entityID) diff --git a/src/EcsQuery.cs b/src/EcsQuery.cs index 35c8143..7bb9e1d 100644 --- a/src/EcsQuery.cs +++ b/src/EcsQuery.cs @@ -1,106 +1,68 @@ using System; using System.Collections.Generic; using System.Reflection; -using System.Runtime.CompilerServices; using Unity.Profiling; -using UnityEngine; -using UnityEngine.UI; namespace DCFApixels.DragonECS { - public abstract class EcsQuery + public abstract class EcsQueryBase { + internal IEcsWorld source; internal EcsGroup groupFilter; internal EcsQueryMask mask; - public IEcsWorld World => groupFilter.World; - - private ProfilerMarker _getEnumerator = new ProfilerMarker("EcsQuery.GetEnumerator"); - - - public EcsGroup.Enumerator GetEnumerator() - { - using (_getEnumerator.Auto()) - { - var pools = World.GetAllPools(); - - EcsReadonlyGroup all = World.Entities; - groupFilter.Clear(); - foreach (var e in all) - { - int entityID = e.id; - - for (int i = 0, iMax = mask.Inc.Length; i < iMax; i++) - { - if (!pools[mask.Inc[i]].Has(entityID)) - { - continue; - } - } - for (int i = 0, iMax = mask.Exc.Length; i < iMax; i++) - { - if (pools[mask.Exc[i]].Has(entityID)) - { - continue; - } - } - groupFilter.AggressiveAdd(entityID); - } - groupFilter.Sort(); - return groupFilter.GetEnumerator(); - } - } - protected virtual void Init(Builder b) { } + public IEcsWorld World => source; #region Builder - public sealed class Builder : EcsQueryBuilder + protected virtual void Init(Builder b) { } + protected abstract void OnBuilt(); + public abstract void Execute(); + public sealed class Builder : EcsQueryBuilderBase { private IEcsWorld _world; private List _inc; private List _exc; - internal static TQuery Build(IEcsWorld world) where TQuery : EcsQuery - { - Builder builder = new Builder(world); - - Type queryType = typeof(TQuery); - ConstructorInfo constructorInfo = queryType.GetConstructor(BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic, null, new Type[] { typeof(Builder) }, null); - EcsQuery newQuery; - if (constructorInfo != null) - { - newQuery = (EcsQuery)constructorInfo.Invoke(new object[] { builder }); - } - else - { - newQuery = (EcsQuery)Activator.CreateInstance(typeof(TQuery)); - newQuery.Init(builder); - } - - builder.End(out newQuery.mask); - // newQuery.groupFilter = new EcsGroup(world); - newQuery.groupFilter = EcsGroup.New(world); - return (TQuery)(object)newQuery; - } - private Builder(IEcsWorld world) { _world = world; _inc = new List(8); _exc = new List(4); } + internal static TQuery Build(IEcsWorld world) where TQuery : EcsQueryBase + { + Builder builder = new Builder(world); + Type queryType = typeof(TQuery); + ConstructorInfo constructorInfo = queryType.GetConstructor(BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic, null, new Type[] { typeof(Builder) }, null); + EcsQueryBase newQuery; + if (constructorInfo != null) + { + newQuery = (EcsQueryBase)constructorInfo.Invoke(new object[] { builder }); + } + else + { + newQuery = (EcsQueryBase)Activator.CreateInstance(typeof(TQuery)); + newQuery.Init(builder); + } + newQuery.groupFilter = EcsGroup.New(world); + newQuery.source = world; + builder.End(out newQuery.mask); + newQuery.OnBuilt(); + return (TQuery)(object)newQuery; + } - public override inc Include() where TComponent : struct + public override inc_ Include() where TComponent : struct { _inc.Add(_world.GetComponentID()); - return new inc(_world.GetPool()); + return new inc_(_world.GetPool()); } - public override exc Exclude() where TComponent : struct + public override exc_ Exclude() where TComponent : struct { _exc.Add(_world.GetComponentID()); - return new exc(_world.GetPool()); + return new exc_(_world.GetPool()); } - public override opt Optional() where TComponent : struct + public override opt_ Optional() where TComponent : struct { - return new opt(_world.GetPool()); + return new opt_(_world.GetPool()); } private void End(out EcsQueryMask mask) @@ -116,6 +78,64 @@ namespace DCFApixels.DragonECS #endregion } + public abstract class EcsJoinQuery : EcsQueryBase + { + private EcsPool attachPool; + + private ProfilerMarker _getEnumerator = new ProfilerMarker("EcsQuery.Execute"); + protected sealed override void OnBuilt() + { + attachPool = World.GetPool(); + } + public sealed override void Execute() + { + using (_getEnumerator.Auto()) + { + throw new NotImplementedException(); + } + } + public EcsGroup.Enumerator GetEnumerator() + { + return groupFilter.GetEnumerator(); + } + } + + public abstract class EcsQuery : EcsQueryBase + { + private ProfilerMarker _getEnumerator = new ProfilerMarker("EcsQuery.Execute"); + protected sealed override void OnBuilt() { } + public sealed override void Execute() + { + using (_getEnumerator.Auto()) + { + var pools = World.GetAllPools(); + + EcsReadonlyGroup all = World.Entities; + groupFilter.Clear(); + foreach (var e in all) + { + int entityID = e.id; + for (int i = 0, iMax = mask.Inc.Length; i < iMax; i++) + { + if (!pools[mask.Inc[i]].Has(entityID)) + continue; + } + for (int i = 0, iMax = mask.Exc.Length; i < iMax; i++) + { + if (pools[mask.Exc[i]].Has(entityID)) + continue; + } + groupFilter.AggressiveAdd(entityID); + } + groupFilter.Sort(); + } + } + public EcsGroup.Enumerator GetEnumerator() + { + return groupFilter.GetEnumerator(); + } + } + public class EcsQueryMask : EcsComponentMask { public EcsQueryMask(Type worldArchetypeType, int[] inc, int[] exc) @@ -125,10 +145,10 @@ namespace DCFApixels.DragonECS Exc = exc; } } - public abstract class EcsQueryBuilder + public abstract class EcsQueryBuilderBase { - public abstract inc Include() where TComponent : struct; - public abstract exc Exclude() where TComponent : struct; - public abstract opt Optional() where TComponent : struct; + public abstract inc_ Include() where TComponent : struct; + public abstract exc_ Exclude() where TComponent : struct; + public abstract opt_ Optional() where TComponent : struct; } } diff --git a/src/EcsQueryMember.cs b/src/EcsQueryMember.cs index 9128cd1..d2f5922 100644 --- a/src/EcsQueryMember.cs +++ b/src/EcsQueryMember.cs @@ -1,89 +1,160 @@ -using System; -using System.Collections.Generic; -using System.Runtime.CompilerServices; +using System.Runtime.CompilerServices; using System.Runtime.InteropServices; namespace DCFApixels.DragonECS { - public interface IEcsQueryMember + public interface IEcsQueryMember { } + public interface IEcsQueryReadonlyField : IEcsQueryMember + { + public ref TComponent Read(ent entityID); + public bool Has(ent entityID); + } + public interface IEcsQueryField : IEcsQueryReadonlyField where TComponent : struct { public ref TComponent Add(ent entityID); public ref TComponent Write(ent entityID); - public ref TComponent Read(ent entityID); - public bool Has(ent entityID); public void Del(ent entityID); } + #region select [StructLayout(LayoutKind.Sequential, Pack = 8, Size = 8)] - public readonly struct inc : IEcsQueryMember + public readonly struct inc_ : IEcsQueryField where TComponent : struct { - private readonly EcsPool _pool; + internal readonly EcsPool pool; [MethodImpl(MethodImplOptions.AggressiveInlining)] - internal inc(EcsPool pool) => _pool = pool; + internal inc_(EcsPool pool) => this.pool = pool; [MethodImpl(MethodImplOptions.AggressiveInlining)] - public ref TComponent Add(ent entityID) => ref _pool.Add(entityID.id); + public ref TComponent Add(ent entityID) => ref pool.Add(entityID.id); [MethodImpl(MethodImplOptions.AggressiveInlining)] - public ref TComponent Write(ent entityID) => ref _pool.Write(entityID.id); + public ref TComponent Write(ent entityID) => ref pool.Write(entityID.id); [MethodImpl(MethodImplOptions.AggressiveInlining)] - public ref TComponent Read(ent entityID) => ref _pool.Read(entityID.id); + public ref TComponent Read(ent entityID) => ref pool.Read(entityID.id); [MethodImpl(MethodImplOptions.AggressiveInlining)] - public bool Has(ent entityID) => _pool.Has(entityID.id); + public bool Has(ent entityID) => pool.Has(entityID.id); [MethodImpl(MethodImplOptions.AggressiveInlining)] - public void Del(ent entityID) => _pool.Del(entityID.id); + public void Del(ent entityID) => pool.Del(entityID.id); [MethodImpl(MethodImplOptions.AggressiveInlining)] - public override string ToString() => $"{(_pool == null ? "NULL" : _pool.World.ArchetypeType.Name)}inc<{typeof(TComponent).Name}>"; - - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static implicit operator inc(EcsQueryBuilder buider) => buider.Include(); + public static implicit operator inc_(EcsQueryBuilderBase buider) => buider.Include(); } [StructLayout(LayoutKind.Sequential, Pack = 8, Size = 8)] - public readonly struct exc : IEcsQueryMember + public readonly struct exc_ : IEcsQueryField where TComponent : struct { - private readonly EcsPool _pool; + internal readonly EcsPool pool; [MethodImpl(MethodImplOptions.AggressiveInlining)] - internal exc(EcsPool pool) => _pool = pool; + internal exc_(EcsPool pool) => this.pool = pool; [MethodImpl(MethodImplOptions.AggressiveInlining)] - public ref TComponent Add(ent entityID) => ref _pool.Add(entityID.id); + public ref TComponent Add(ent entityID) => ref pool.Add(entityID.id); [MethodImpl(MethodImplOptions.AggressiveInlining)] - public ref TComponent Write(ent entityID) => ref _pool.Write(entityID.id); + public ref TComponent Write(ent entityID) => ref pool.Write(entityID.id); [MethodImpl(MethodImplOptions.AggressiveInlining)] - public ref TComponent Read(ent entityID) => ref _pool.Read(entityID.id); + public ref TComponent Read(ent entityID) => ref pool.Read(entityID.id); [MethodImpl(MethodImplOptions.AggressiveInlining)] - public bool Has(ent entityID) => _pool.Has(entityID.id); + public bool Has(ent entityID) => pool.Has(entityID.id); [MethodImpl(MethodImplOptions.AggressiveInlining)] - public void Del(ent entityID) => _pool.Del(entityID.id); + public void Del(ent entityID) => pool.Del(entityID.id); [MethodImpl(MethodImplOptions.AggressiveInlining)] - public override string ToString() => $"{(_pool == null ? "NULL" : _pool.World.ArchetypeType.Name)}exc<{typeof(TComponent).Name}>"; - - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static implicit operator exc(EcsQueryBuilder buider) => buider.Exclude(); + public static implicit operator exc_(EcsQueryBuilderBase buider) => buider.Exclude(); } [StructLayout(LayoutKind.Sequential, Pack = 8, Size = 8)] - public readonly struct opt : IEcsQueryMember + public readonly struct opt_ : IEcsQueryField where TComponent : struct { - private readonly EcsPool _pool; + internal readonly EcsPool pool; [MethodImpl(MethodImplOptions.AggressiveInlining)] - internal opt(EcsPool pool) => _pool = pool; + internal opt_(EcsPool pool) => this.pool = pool; [MethodImpl(MethodImplOptions.AggressiveInlining)] - public ref TComponent Add(ent entityID) => ref _pool.Add(entityID.id); + public ref TComponent Add(ent entityID) => ref pool.Add(entityID.id); [MethodImpl(MethodImplOptions.AggressiveInlining)] - public ref TComponent Write(ent entityID) => ref _pool.Write(entityID.id); + public ref TComponent Write(ent entityID) => ref pool.Write(entityID.id); [MethodImpl(MethodImplOptions.AggressiveInlining)] - public ref TComponent Read(ent entityID) => ref _pool.Read(entityID.id); + public ref TComponent Read(ent entityID) => ref pool.Read(entityID.id); [MethodImpl(MethodImplOptions.AggressiveInlining)] - public bool Has(ent entityID) => _pool.Has(entityID.id); + public bool Has(ent entityID) => pool.Has(entityID.id); [MethodImpl(MethodImplOptions.AggressiveInlining)] - public void Del(ent entityID) => _pool.Del(entityID.id); + public void Del(ent entityID) => pool.Del(entityID.id); [MethodImpl(MethodImplOptions.AggressiveInlining)] - public override string ToString() => $"{(_pool == null ? "NULL" : _pool.World.ArchetypeType.Name)}opt<{typeof(TComponent).Name}>"; - - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static implicit operator opt(EcsQueryBuilder buider) => buider.Optional(); + public static implicit operator opt_(EcsQueryBuilderBase buider) => buider.Optional(); } + #endregion + + // #region select_readonly + // [StructLayout(LayoutKind.Sequential, Pack = 8, Size = 8)] + // public readonly struct inc_readonly_ : IEcsQueryReadonlyField + // where TComponent : struct + // { + // internal readonly EcsPool pool; + // [MethodImpl(MethodImplOptions.AggressiveInlining)] + // internal inc_readonly_(EcsPool pool) => this.pool = pool; + // [MethodImpl(MethodImplOptions.AggressiveInlining)] + // public ref TComponent Read(ent entityID) => ref pool.Read(entityID.id); + // [MethodImpl(MethodImplOptions.AggressiveInlining)] + // public bool Has(ent entityID) => pool.Has(entityID.id); + // [MethodImpl(MethodImplOptions.AggressiveInlining)] + // + // public static implicit operator inc_readonly_(EcsQueryBuilderBase buider) => buider.Include(); + // public static implicit operator inc_readonly_(inc_ o) => new inc_readonly_(o.pool); + // } + // + // [StructLayout(LayoutKind.Sequential, Pack = 8, Size = 8)] + // public readonly struct exc_readonly_ : IEcsQueryReadonlyField + // where TComponent : struct + // { + // internal readonly EcsPool pool; + // [MethodImpl(MethodImplOptions.AggressiveInlining)] + // internal exc_readonly_(EcsPool pool) => this.pool = pool; + // [MethodImpl(MethodImplOptions.AggressiveInlining)] + // public ref TComponent Read(ent entityID) => ref pool.Read(entityID.id); + // [MethodImpl(MethodImplOptions.AggressiveInlining)] + // public bool Has(ent entityID) => pool.Has(entityID.id); + // [MethodImpl(MethodImplOptions.AggressiveInlining)] + // + // public static implicit operator exc_readonly_(EcsQueryBuilderBase buider) => buider.Exclude(); + // public static implicit operator exc_readonly_(exc_ o) => new exc_readonly_(o.pool); + // } + // + // [StructLayout(LayoutKind.Sequential, Pack = 8, Size = 8)] + // public readonly struct opt_readonly_ : IEcsQueryReadonlyField + // where TComponent : struct + // { + // internal readonly EcsPool pool; + // [MethodImpl(MethodImplOptions.AggressiveInlining)] + // internal opt_readonly_(EcsPool pool) => this.pool = pool; + // [MethodImpl(MethodImplOptions.AggressiveInlining)] + // public ref TComponent Read(ent entityID) => ref pool.Read(entityID.id); + // [MethodImpl(MethodImplOptions.AggressiveInlining)] + // public bool Has(ent entityID) => pool.Has(entityID.id); + // [MethodImpl(MethodImplOptions.AggressiveInlining)] + // + // public static implicit operator opt_readonly_(EcsQueryBuilderBase buider) => buider.Optional(); + // public static implicit operator opt_readonly_(opt_ o) => new opt_readonly_(o.pool); + // } + // #endregion + // + // #region join + // [StructLayout(LayoutKind.Sequential, Pack = 8, Size = 8)] + // public readonly struct attach : IEcsQueryField + // { + // internal readonly EcsPool pool; + // [MethodImpl(MethodImplOptions.AggressiveInlining)] + // internal attach(EcsPool pool) => this.pool = pool; + // [MethodImpl(MethodImplOptions.AggressiveInlining)] + // public ref Attach Add(ent entityID) => ref pool.Add(entityID.id); + // [MethodImpl(MethodImplOptions.AggressiveInlining)] + // public ref Attach Write(ent entityID) => ref pool.Write(entityID.id); + // [MethodImpl(MethodImplOptions.AggressiveInlining)] + // public ref Attach Read(ent entityID) => ref pool.Read(entityID.id); + // [MethodImpl(MethodImplOptions.AggressiveInlining)] + // public bool Has(ent entityID) => pool.Has(entityID.id); + // [MethodImpl(MethodImplOptions.AggressiveInlining)] + // public void Del(ent entityID) => pool.Del(entityID.id); + // [MethodImpl(MethodImplOptions.AggressiveInlining)] + // public static implicit operator attach(EcsQueryBuilderBase buider) => buider.Include(); + // public static implicit operator attach(inc_ o) => new attach(o.pool); + // } + // #endregion } diff --git a/src/EcsWorld.cs b/src/EcsWorld.cs index 99f65e1..2ced962 100644 --- a/src/EcsWorld.cs +++ b/src/EcsWorld.cs @@ -38,9 +38,9 @@ namespace DCFApixels.DragonECS public readonly short id; - protected EcsWorld(bool isIndexed) + protected EcsWorld(bool isIndexable) { - if(isIndexed == true) + if(isIndexable == true) { id = (short)_worldIdDispenser.GetFree(); if (id >= Worlds.Length) @@ -74,11 +74,11 @@ namespace DCFApixels.DragonECS private EcsPool[] _pools; private EcsNullPool _nullPool; - private EcsQuery[] _queries; + private EcsQueryBase[] _queries; private EcsPipeline _pipeline; - private List _groups; + private List> _groups; public IEcsRealationTable[] _relationTables; @@ -132,7 +132,8 @@ namespace DCFApixels.DragonECS #endregion #region Constructors - public EcsWorld(EcsPipeline pipline = null) : base(true) + public EcsWorld(EcsPipeline pipline = null) : this(pipline, true) { } + internal EcsWorld(EcsPipeline pipline, bool isIndexable) : base(isIndexable) { _pipeline = pipline ?? EcsPipeline.Empty; if (!_pipeline.IsInit) pipline.Init(); @@ -143,7 +144,7 @@ namespace DCFApixels.DragonECS _gens = new short[512]; _queries = new EcsQuery[QueryType.capacity]; - _groups = new List(128); + _groups = new List>(); _denseEntities = new int[512]; @@ -153,8 +154,8 @@ namespace DCFApixels.DragonECS _pipeline.GetRunner>().Inject((TWorldArchetype)this); _pipeline.GetRunner>().Inject(this); _pipeline.GetRunner().OnWorldCreate(this); - - _allEntites = new EcsGroup(this); + + _allEntites = GetGroupFromPool(); } #endregion @@ -181,17 +182,15 @@ namespace DCFApixels.DragonECS #endregion #region Query - public TQuery Query(out TQuery query) where TQuery : EcsQuery + public TQuery Query(out TQuery query) where TQuery : EcsQueryBase { int uniqueID = QueryType.uniqueID; if (_queries.Length < QueryType.capacity) Array.Resize(ref _queries, QueryType.capacity); - if (_queries[uniqueID] == null) - { - _queries[uniqueID] = EcsQuery.Builder.Build(this); - } + _queries[uniqueID] = EcsQueryBase.Builder.Build(this); query = (TQuery)_queries[uniqueID]; + query.Execute(); return query; } #endregion @@ -233,8 +232,19 @@ namespace DCFApixels.DragonECS if (_gens.Length <= entityID) { Array.Resize(ref _gens, _gens.Length << 1); - foreach (var item in _groups) - item.OnWorldResize(_gens.Length); + for (int i = 0; i < _groups.Count; i++) + { + if (_groups[i].TryGetTarget(out EcsGroup group)) + { + group.OnWorldResize(_gens.Length); + } + else + { + int last = _groups.Count - 1; + _groups[i--] = _groups[last]; + _groups.RemoveAt(last); + } + } foreach (var item in _pools) item.OnWorldResize(_gens.Length); } @@ -286,7 +296,7 @@ namespace DCFApixels.DragonECS #region Other void IEcsReadonlyTable.RegisterGroup(EcsGroup group) { - _groups.Add(group); + _groups.Add(new WeakReference(group)); } #endregion @@ -339,7 +349,8 @@ namespace DCFApixels.DragonECS #region GroupsPool private Stack _pool = new Stack(64); - EcsGroup IEcsWorld.GetGroupFromPool() + EcsGroup IEcsWorld.GetGroupFromPool() => GetGroupFromPool(); + internal EcsGroup GetGroupFromPool() { if (_pool.Count <= 0) return new EcsGroup(this); diff --git a/src/Entities/EcsEntity.cs b/src/Entities/EcsEntity.cs index 00059e9..3e5b476 100644 --- a/src/Entities/EcsEntity.cs +++ b/src/Entities/EcsEntity.cs @@ -4,8 +4,7 @@ using System.Runtime.InteropServices; namespace DCFApixels.DragonECS { - - /// Permanent relation entity identifier + /// Strong identifier/Permanent entity identifier [StructLayout(LayoutKind.Explicit, Pack = 2, Size = 8)] public readonly partial struct EcsEntity : IEquatable, IEquatable { diff --git a/src/Entities/EcsRelation.cs b/src/Entities/EcsRelation.cs deleted file mode 100644 index ed55ff2..0000000 --- a/src/Entities/EcsRelation.cs +++ /dev/null @@ -1,74 +0,0 @@ -using System; -using System.Runtime.CompilerServices; -using System.Runtime.InteropServices; - -namespace DCFApixels.DragonECS -{ - /// Permanent relation entity identifier - [StructLayout(LayoutKind.Explicit, Pack = 2, Size = 8)] - public readonly partial struct EcsRelation : IEquatable, IEquatable - { - public static readonly EcsEntity NULL = default; - // uniqueID - 32 bits - // gen - 16 bits - // world - 16 bits - [FieldOffset(0)] - internal readonly long full; //Union - [FieldOffset(3)] - public readonly int id; - [FieldOffset(1)] - public readonly short rightWorld; - [FieldOffset(0)] - public readonly short leftWorld; - - public ent Ent - { - [MethodImpl(MethodImplOptions.AggressiveInlining)] - get => new ent(id); - } - - #region Constructors - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public EcsRelation(int id, short leftWorld, short rightWorld) : this() - { - this.id = id; - this.leftWorld = leftWorld; - this.rightWorld = rightWorld; - } - [MethodImpl(MethodImplOptions.AggressiveInlining)] - internal EcsRelation(long full) : this() - { - this.full = full; - } - #endregion - - #region Equals - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public bool Equals(EcsRelation other) => full == other.full; - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public bool Equals(long other) => full == other; - #endregion - - #region Object - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public override int GetHashCode() => unchecked((int)full) ^ (int)(full >> 32); - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public override string ToString() => $"Relation(id:{id} leftWorld:{leftWorld} rightWorld:{rightWorld})"; - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public override bool Equals(object obj) => obj is EcsRelation other && full == other.full; - #endregion - - #region operators - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static bool operator ==(in EcsRelation a, in EcsRelation b) => a.full == b.full; - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static bool operator !=(in EcsRelation a, in EcsRelation b) => a.full != b.full; - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static explicit operator long(in EcsRelation a) => a.full; - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static explicit operator ent(in EcsRelation a) => a.Ent; - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static explicit operator EcsRelation(in long a) => new EcsRelation(a); - #endregion - } -} diff --git a/src/Entities/ent.cs b/src/Entities/ent.cs index c5ddd38..160744a 100644 --- a/src/Entities/ent.cs +++ b/src/Entities/ent.cs @@ -4,8 +4,8 @@ using UnityEngine.Rendering; namespace DCFApixels.DragonECS { -#pragma warning disable CS0660, CS0661 - /// Single frame entity identifier +#pragma warning disable CS0660, CS0661, IDE1006 + /// Weak identifier/Single frame entity identifier [StructLayout(LayoutKind.Sequential, Pack = 4, Size = 4)] public readonly ref struct ent { @@ -25,5 +25,8 @@ namespace DCFApixels.DragonECS public static bool operator !=(Null? _, ent b) => b.id != 0; public struct Null { } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public EcsEntity ToStrong(IEcsWorld world) => world.GetEntity(id); } } diff --git a/src/Interfaces/IEcsReadonlyTable.cs b/src/Interfaces/IEcsReadonlyTable.cs index ec2b50d..fcb7a24 100644 --- a/src/Interfaces/IEcsReadonlyTable.cs +++ b/src/Interfaces/IEcsReadonlyTable.cs @@ -15,7 +15,7 @@ namespace DCFApixels.DragonECS #region Methods public EcsPool GetPool() where T : struct; public ReadOnlySpan GetAllPools(); - public TQuery Query(out TQuery query) where TQuery : EcsQuery; + public TQuery Query(out TQuery query) where TQuery : EcsQueryBase; public int GetComponentID(); public bool IsMaskCompatible(int entityID) where TInc : struct, IInc where TExc : struct, IExc; diff --git a/src/Utils/SparseArray.cs b/src/Utils/SparseArray.cs new file mode 100644 index 0000000..e89ec0c --- /dev/null +++ b/src/Utils/SparseArray.cs @@ -0,0 +1,203 @@ +using System; +using System.Diagnostics.Contracts; +using System.Runtime.CompilerServices; +using System.Runtime.InteropServices; + +namespace DCFApixels +{ + public class SparseArray + { + public const int MIN_CAPACITY = 16; + private const int EMPTY = -1; + + private int[] _buckets = Array.Empty(); + private Entry[] _entries = Array.Empty(); + + private int _count; + + private int _freeList; + private int _freeCount; + + #region Properties + public TValue this[int key] + { + [MethodImpl(MethodImplOptions.AggressiveInlining)] + get => _entries[FindEntry(key)].value; + [MethodImpl(MethodImplOptions.AggressiveInlining)] + set => Insert(key, value); + } + + public int Count => _count; + #endregion + + #region Constructors + public SparseArray(int capacity = MIN_CAPACITY) + { + _buckets = new int[capacity]; + for (int i = 0; i < capacity; i++) + _buckets[i] = EMPTY; + _entries = new Entry[capacity]; + } + #endregion + + #region Add + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public void Add(int key, TValue value) + { +#if DEBUG + if (Contains(key)) + throw new ArgumentException("Contains(hashKey) is true"); +#endif + Insert(key, value); + } + #endregion + + #region Find/Insert/Remove + private int FindEntry(int key) + { + key &= 0x7FFFFFFF; + for (int i = _buckets[key % _buckets.Length]; i >= 0; i = _entries[i].next) + { + if (_entries[i].hashKey == key) return i; + } + return -1; + } + private void Insert(int key, TValue value) + { + key &= 0x7FFFFFFF; + int targetBucket = key % _buckets.Length; + + for (int i = _buckets[targetBucket]; i >= 0; i = _entries[i].next) + { + if (_entries[i].hashKey == key) + { + _entries[i].value = value; + return; + } + } + + int index; + if (_freeCount > 0) + { + index = _freeList; + _freeList = _entries[index].next; + _freeCount--; + } + else + { + if (_count == _entries.Length) + { + Resize(); + targetBucket = key % _buckets.Length; + } + index = _count; + _count++; + } + + _entries[index].next = _buckets[targetBucket]; + _entries[index].hashKey = key; + _entries[index].value = value; + _buckets[targetBucket] = index; + } + public bool Remove(int key) + { + key &= 0x7FFFFFFF; + int bucket = key % _buckets.Length; + int last = -1; + for (int i = _buckets[bucket]; i >= 0; last = i, i = _entries[i].next) + { + if (_entries[i].hashKey == key) + { + if (last < 0) + { + _buckets[bucket] = _entries[i].next; + } + else + { + _entries[last].next = _entries[i].next; + } + _entries[i].next = _freeList; + _entries[i].hashKey = -1; + _entries[i].value = default; + _freeList = i; + _freeCount++; + return true; + } + } + return false; + } + #endregion + + #region TryGetValue + public bool TryGetValue(int key, out TValue value) + { + int index = FindEntry(key); + if (index < 0) + { + value = default; + return false; + } + value = _entries[index].value; + return true; + } + #endregion + + #region Contains + public bool Contains(int key) + { + return FindEntry(key) >= 0; + } + #endregion + + #region Clear + public void Clear() + { + if (_count > 0) + { + for (int i = 0; i < _buckets.Length; i++) + { + _buckets[i] = -1; + } + Array.Clear(_entries, 0, _count); + _count = 0; + } + } + #endregion + + #region Resize + private void Resize() + { + int newSize = _buckets.Length << 1; + + Contract.Assert(newSize >= _entries.Length); + int[] newBuckets = new int[newSize]; + for (int i = 0; i < newBuckets.Length; i++) + newBuckets[i] = EMPTY; + + Entry[] newEntries = new Entry[newSize]; + Array.Copy(_entries, 0, newEntries, 0, _count); + for (int i = 0; i < _count; i++) + { + if (newEntries[i].hashKey >= 0) + { + int bucket = newEntries[i].hashKey % newSize; + newEntries[i].next = newBuckets[bucket]; + newBuckets[bucket] = i; + } + } + _buckets = newBuckets; + _entries = newEntries; + } + #endregion + + #region Utils + [StructLayout(LayoutKind.Sequential, Pack = 4)] + private struct Entry + { + public int next; // Index of next entry, -1 if last + public int hashKey; + public TValue value; + } + #endregion + } +} \ No newline at end of file