From 3119ea42dbff541e7fb0ed0c1d260a97a3616492 Mon Sep 17 00:00:00 2001 From: Mikhail <99481254+DCFApixels@users.noreply.github.com> Date: Fri, 5 Jan 2024 22:16:48 +0800 Subject: [PATCH 01/11] update collections --- src/Collections/EcsGroup.cs | 20 ++++++++++---- src/Collections/EcsSpan.cs | 54 ++++++++++++++++++++++++++++--------- 2 files changed, 57 insertions(+), 17 deletions(-) diff --git a/src/Collections/EcsGroup.cs b/src/Collections/EcsGroup.cs index 51fd4ed..46138bf 100644 --- a/src/Collections/EcsGroup.cs +++ b/src/Collections/EcsGroup.cs @@ -2,6 +2,7 @@ using System; using System.Collections; using System.Collections.Generic; +using System.ComponentModel; using System.Diagnostics; using System.Linq; using System.Runtime.CompilerServices; @@ -111,10 +112,6 @@ namespace DCFApixels.DragonECS public bool IsSupersetOf(EcsGroup group) => _source.IsSupersetOf(group); #endregion - #region Object - public override string ToString() => _source != null ? _source.ToString() : "NULL"; - #endregion - #region Internal [MethodImpl(MethodImplOptions.AggressiveInlining)] internal EcsGroup GetGroupInternal() => _source; @@ -122,6 +119,19 @@ namespace DCFApixels.DragonECS #endregion #region Other + public override string ToString() + { + return _source != null ? _source.ToString() : "NULL"; + } +#pragma warning disable CS0809 // Устаревший член переопределяет неустаревший член + [Obsolete("Equals() on EcsGroup will always throw an exception. Use the equality operator instead.")] + [EditorBrowsable(EditorBrowsableState.Never)] + public override bool Equals(object obj) => throw new NotSupportedException(); + [Obsolete("GetHashCode() on EcsGroup will always throw an exception.")] + [EditorBrowsable(EditorBrowsableState.Never)] + public override int GetHashCode() => throw new NotSupportedException(); +#pragma warning restore CS0809 // Устаревший член переопределяет неустаревший член + [MethodImpl(MethodImplOptions.AggressiveInlining)] public static implicit operator EcsSpan(EcsReadonlyGroup a) => a.ToSpan(); internal class DebuggerProxy : EcsGroup.DebuggerProxy @@ -710,7 +720,7 @@ namespace DCFApixels.DragonECS #region Other public override string ToString() { - return $"group{{{string.Join(", ", _dense.Skip(1).Take(_count).OrderBy(o => o))}}}"; + return $"group({_count}) {{{string.Join(", ", _dense.Skip(1).Take(_count).OrderBy(o => o))}}}"; } [MethodImpl(MethodImplOptions.AggressiveInlining)] public int First() diff --git a/src/Collections/EcsSpan.cs b/src/Collections/EcsSpan.cs index d95f773..38e22a3 100644 --- a/src/Collections/EcsSpan.cs +++ b/src/Collections/EcsSpan.cs @@ -1,10 +1,13 @@ using System; using System.Collections.Generic; using System.ComponentModel; +using System.Diagnostics; +using System.Linq; using System.Runtime.CompilerServices; namespace DCFApixels.DragonECS { + [DebuggerTypeProxy(typeof(DebuggerProxy))] public readonly ref struct EcsSpan { private readonly int _worldID; @@ -91,18 +94,6 @@ namespace DCFApixels.DragonECS } #endregion - #region Object -#pragma warning disable CS0809 // Устаревший член переопределяет неустаревший член - [Obsolete("Equals() on EcsSpan will always throw an exception. Use the equality operator instead.")] - [EditorBrowsable(EditorBrowsableState.Never)] - public override bool Equals(object obj) => throw new NotSupportedException(); - [Obsolete("GetHashCode() on EcsSpan will always throw an exception.")] - [EditorBrowsable(EditorBrowsableState.Never)] - public override int GetHashCode() => throw new NotSupportedException(); -#pragma warning restore CS0809 // Устаревший член переопределяет неустаревший член - public override string ToString() => _values.ToString(); - #endregion - #region operators public static bool operator ==(EcsSpan left, EcsSpan right) => left._values == right._values; public static bool operator !=(EcsSpan left, EcsSpan right) => left._values != right._values; @@ -120,6 +111,45 @@ namespace DCFApixels.DragonECS public EcsSpan Slice(int start, int length) => new EcsSpan(_worldID, _values.Slice(start, length)); [MethodImpl(MethodImplOptions.AggressiveInlining)] public int[] ToArray() => _values.ToArray(); + +#pragma warning disable CS0809 // Устаревший член переопределяет неустаревший член + [Obsolete("Equals() on EcsSpan will always throw an exception. Use the equality operator instead.")] + [EditorBrowsable(EditorBrowsableState.Never)] + public override bool Equals(object obj) => throw new NotSupportedException(); + [Obsolete("GetHashCode() on EcsSpan will always throw an exception.")] + [EditorBrowsable(EditorBrowsableState.Never)] + public override int GetHashCode() => throw new NotSupportedException(); +#pragma warning restore CS0809 // Устаревший член переопределяет неустаревший член + public override string ToString() + { + return $"span({_values.Length}) {{{string.Join(", ", _values.ToArray().OrderBy(o => o))}}}"; + } + + internal class DebuggerProxy + { + private int[] _values; + private int _worldID; + public EcsWorld World => EcsWorld.GetWorld(_worldID); + public entlong[] Entities + { + get + { + entlong[] result = new entlong[_values.Length]; + int i = 0; + foreach (var e in _values) + result[i++] = World.GetEntityLong(e); + return result; + } + } + public int Count => _values.Length; + //public override string ToString() => $"span({_values.Length}) {{{string.Join(", ", _values.OrderBy(o => o))}}}"; + public DebuggerProxy(EcsSpan span) + { + _values = new int[span.Length]; + span._values.CopyTo(_values); + _worldID = span._worldID; + } + } #endregion } } From fc167fb28c4ea96a75926b763e70709020a4060f Mon Sep 17 00:00:00 2001 From: Mikhail <99481254+DCFApixels@users.noreply.github.com> Date: Fri, 5 Jan 2024 23:49:29 +0800 Subject: [PATCH 02/11] update collectiona --- src/Collections/EcsGroup.cs | 15 +++++++++------ src/Collections/EcsSpan.cs | 8 ++++---- src/Utils/ArrayUtility.cs | 14 ++++++++++++-- 3 files changed, 25 insertions(+), 12 deletions(-) diff --git a/src/Collections/EcsGroup.cs b/src/Collections/EcsGroup.cs index 46138bf..d95d92d 100644 --- a/src/Collections/EcsGroup.cs +++ b/src/Collections/EcsGroup.cs @@ -1,4 +1,5 @@ using DCFApixels.DragonECS.Internal; +using DCFApixels.DragonECS.Utils; using System; using System.Collections; using System.Collections.Generic; @@ -13,7 +14,7 @@ namespace DCFApixels.DragonECS //_dense заполняется с индекса 1 //в операциях изменяющих состояние группы нельзя итерироваться по this, либо осторожно учитывать этот момент [StructLayout(LayoutKind.Sequential, Pack = 0, Size = 8)] - [DebuggerTypeProxy(typeof(DebuggerProxy))] + [DebuggerTypeProxy(typeof(EcsGroup.DebuggerProxy))] public readonly ref struct EcsReadonlyGroup { private readonly EcsGroup _source; @@ -134,10 +135,10 @@ namespace DCFApixels.DragonECS [MethodImpl(MethodImplOptions.AggressiveInlining)] public static implicit operator EcsSpan(EcsReadonlyGroup a) => a.ToSpan(); - internal class DebuggerProxy : EcsGroup.DebuggerProxy - { - public DebuggerProxy(EcsReadonlyGroup group) : base(group._source) { } - } + //internal class DebuggerProxy : EcsGroup.DebuggerProxy + //{ + // public DebuggerProxy(EcsReadonlyGroup group) : base(group._source) { } + //} #endregion } @@ -720,7 +721,8 @@ namespace DCFApixels.DragonECS #region Other public override string ToString() { - return $"group({_count}) {{{string.Join(", ", _dense.Skip(1).Take(_count).OrderBy(o => o))}}}"; + return EntitiesCollectionUtility.AutoToString(_dense.Skip(1).Take(_count), "group"); + //return $"group({_count}) {{{string.Join(", ", _dense.Skip(1).Take(_count).OrderBy(o => o))}}}"; } [MethodImpl(MethodImplOptions.AggressiveInlining)] public int First() @@ -762,6 +764,7 @@ namespace DCFApixels.DragonECS public int CapacitySparce => _group.CapacitySparce; public override string ToString() => _group.ToString(); public DebuggerProxy(EcsGroup group) => _group = group; + public DebuggerProxy(EcsReadonlyGroup group) : this(group.GetGroupInternal()) { } } #endregion } diff --git a/src/Collections/EcsSpan.cs b/src/Collections/EcsSpan.cs index 38e22a3..6a943df 100644 --- a/src/Collections/EcsSpan.cs +++ b/src/Collections/EcsSpan.cs @@ -1,8 +1,8 @@ -using System; +using DCFApixels.DragonECS.Utils; +using System; using System.Collections.Generic; using System.ComponentModel; using System.Diagnostics; -using System.Linq; using System.Runtime.CompilerServices; namespace DCFApixels.DragonECS @@ -122,7 +122,8 @@ namespace DCFApixels.DragonECS #pragma warning restore CS0809 // Устаревший член переопределяет неустаревший член public override string ToString() { - return $"span({_values.Length}) {{{string.Join(", ", _values.ToArray().OrderBy(o => o))}}}"; + return EntitiesCollectionUtility.AutoToString(_values.ToArray(), "span"); + //return $"span({_values.Length}) {{{string.Join(", ", _values.ToArray().OrderBy(o => o))}}}"; } internal class DebuggerProxy @@ -142,7 +143,6 @@ namespace DCFApixels.DragonECS } } public int Count => _values.Length; - //public override string ToString() => $"span({_values.Length}) {{{string.Join(", ", _values.OrderBy(o => o))}}}"; public DebuggerProxy(EcsSpan span) { _values = new int[span.Length]; diff --git a/src/Utils/ArrayUtility.cs b/src/Utils/ArrayUtility.cs index 7ad4540..5ff3c32 100644 --- a/src/Utils/ArrayUtility.cs +++ b/src/Utils/ArrayUtility.cs @@ -1,6 +1,8 @@ -using System.Runtime.CompilerServices; +using System; +using System.Collections.Generic; +using System.Linq; +using System.Runtime.CompilerServices; using System.Runtime.InteropServices; -using System; namespace DCFApixels.DragonECS.Utils { @@ -51,4 +53,12 @@ namespace DCFApixels.DragonECS.Utils new IntPtr(Marshal.SizeOf(typeof(T)) * newCount))).ToPointer(); } } + + public static class EntitiesCollectionUtility + { + public static string AutoToString(IEnumerable range, string name) + { + return $"{name}({range.Count()}) {{{string.Join(", ", range.OrderBy(o => o))}}})"; + } + } } From be2ecb8c072344ce5506a2adc7f8c9bcbc87118e Mon Sep 17 00:00:00 2001 From: Mikhail <99481254+DCFApixels@users.noreply.github.com> Date: Sat, 6 Jan 2024 00:07:07 +0800 Subject: [PATCH 03/11] sorting pools in query iterator sorting pools in the iterator. by number of entities from the smallest to the largest. --- src/EcsAspect.cs | 353 ++++++++++++++++++++++++++++++++++++++++-- src/EcsWorld.cs | 5 +- src/EcsWorld.pools.cs | 20 ++- 3 files changed, 361 insertions(+), 17 deletions(-) diff --git a/src/EcsAspect.cs b/src/EcsAspect.cs index 2fb9ee4..1db879b 100644 --- a/src/EcsAspect.cs +++ b/src/EcsAspect.cs @@ -1,4 +1,9 @@ -using DCFApixels.DragonECS.Internal; +//#define sort_1 +#define lockSwapCheck +// Зеленый свет, тесты показали идентичную скорость или даже прирост + +using DCFApixels.DragonECS.Internal; +using DCFApixels.DragonECS.Utils; using System; using System.Collections.Generic; using System.Diagnostics; @@ -215,6 +220,7 @@ namespace DCFApixels.DragonECS #endregion #region Mask + [DebuggerTypeProxy(typeof(DebuggerProxy))] public readonly struct EcsMaskBit { private const int BITS = 32; @@ -228,14 +234,32 @@ namespace DCFApixels.DragonECS this.chankIndex = chankIndex; this.mask = mask; } + [MethodImpl(MethodImplOptions.AggressiveInlining)] public static EcsMaskBit FromID(int id) { - short x = 10; return new EcsMaskBit(id >> DIV_SHIFT, 1 << (id & MOD_MASK)); //аналогично new EcsMaskBit(id / BITS, 1 << (id % BITS)) но быстрее } public override string ToString() { - return $"bit({chankIndex}, {mask})"; + return $"mask({chankIndex}, {mask}, {BitsUtility.CountBits(mask)})"; + } + internal class DebuggerProxy + { + public int chunk; + public uint mask; + public int[] values = Array.Empty(); + public string bits; + public DebuggerProxy(EcsMaskBit maskbits) + { + chunk = maskbits.chankIndex; + mask = (uint)maskbits.mask; + BitsUtility.GetBitNumbersNoAlloc(mask, ref values); + for (int i = 0; i < values.Length; i++) + { + values[i] += (chunk) << 5; + } + bits = BitsUtility.ToBitsString(mask, '_', 8); + } } } @@ -248,6 +272,7 @@ namespace DCFApixels.DragonECS internal readonly int[] inc; internal readonly int[] exc; public int WorldID => worldID; + public EcsWorld World => EcsWorld.GetWorld(worldID); /// Including constraints public ReadOnlySpan Inc => inc; /// Excluding constraints @@ -405,24 +430,302 @@ namespace DCFApixels.DragonECS [MethodImpl(MethodImplOptions.AggressiveInlining)] public Enumerator GetEnumerator() => new Enumerator(_span, mask); - public ref struct Enumerator + + public unsafe ref struct Enumerator { private ReadOnlySpan.Enumerator _span; +#if sort_1 private readonly EcsMaskBit[] _incChunckMasks; private readonly EcsMaskBit[] _excChunckMasks; +#else + private readonly EcsMaskBit[] _incChunckMasks; + private readonly EcsMaskBit[] _excChunckMasks; +#endif private readonly int[][] _entitiesComponentMasks; - private int minCount = 0; - public Enumerator(EcsSpan span, EcsMask mask) + + private static EcsMaskBit* _sortedIncBuffer; + private static EcsMaskBit* _sortedExcBuffer; + private static SparseArray _sp; + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public unsafe Enumerator(EcsSpan span, EcsMask mask) { _span = span.GetEnumerator(); +#if sort_1 _incChunckMasks = mask.incChunckMasks; _excChunckMasks = mask.excChunckMasks; +#endif _entitiesComponentMasks = span.World._entitiesComponentMasks; + #region MyRegion + #region Trash +#if !sort_1 + int[] inc = mask.inc; + int[] exc = mask.exc; + int[] counts = mask.World._poolComponentCounts; + if (inc.Length > 1) + { + //if (inc.Length == 2) + //{ + // if (counts[inc[0]] > counts[inc[1]]) + // { + // int tmp = inc[0]; + // inc[0] = inc[1]; + // inc[1] = tmp; + // } + // //... + //} + //else + { + //for (int i = 0; i < inc.Length; i++) + //{ + // int counti = counts[inc[i]]; + // if (counti <= 0) + // { + // _span = ReadOnlySpan.Empty.GetEnumerator(); + // goto skip1; + // } + // for (int j = inc.Length - 1; j >= i; j--) + // { + // if (counts[inc[j]] < counti) + // { + // int tmp = inc[j]; + // inc[j] = inc[i]; + // inc[i] = tmp; + // } + // } + //} + + //... + + for (int i = 0, n = inc.Length; i < n - 1; i++) + { + //int counti = counts[inc[i]]; + //if (counti <= 0) + //{ + // _span = ReadOnlySpan.Empty.GetEnumerator(); + // goto skip1; + //} + bool noSwaped = true; + for (int j = 0; j < n - i - 1; ) + { + ref int j0 = ref inc[j++]; + if (counts[j0] > counts[inc[j]]) + { + int tmp = inc[j]; + inc[j] = j0; + j0 = tmp; + noSwaped = false; + } + } +#if !lockSwapCheck + if (noSwaped) + break; +#endif + } + } + } + skip1:; + if (exc.Length > 1) + { + //if (exc.Length == 2) + //{ + // if (counts[exc[0]] < counts[exc[1]]) + // { + // int tmp = exc[0]; + // exc[0] = inc[1]; + // exc[1] = tmp; + // } + // //... + //} + //else + { + //for (int i = 0; i < exc.Length; i++) + //{ + // int counti = counts[inc[i]]; + // if (counti <= 0) + // { + // _excChunckMasks = ReadOnlySpan.Empty; + // goto skip2; + // } + // for (int j = exc.Length - 1; j >= i; j--) + // { + // if (counts[exc[j]] > counti) + // { + // int tmp = exc[j]; + // exc[j] = exc[i]; + // exc[i] = tmp; + // } + // } + //} + + //... + + for (int i = 0, n = exc.Length; i < n - 1; i++) + { + //int counti = counts[inc[i]]; + //if (counti <= 0) + //{ + // _excChunckMasks = ReadOnlySpan.Empty; + // goto skip2; + //} + bool noSwaped = true; + for (int j = 0; j < n - i - 1;) + { + ref int j0 = ref exc[j++]; + if (counts[j0] < counts[exc[j]]) + { + int tmp = exc[j]; + exc[j] = j0; + j0 = tmp; + noSwaped = false; + } + } +#if !lockSwapCheck + if (noSwaped) + break; +#endif + } + } + } + skip2:; + + if (_sortedIncBuffer == null) + { + _sortedIncBuffer = UnmanagedArrayUtility.New(256); + _sortedExcBuffer = UnmanagedArrayUtility.New(256); + _sp = new SparseArray(32); + } + + _sp.Clear(); + for (int i = 0, ii = 0; i < inc.Length; i++) + { + //int id = inc[i]; + //_sortedIncBuffer[i] = new EcsMaskBit(id >> DIV_SHIFT, 1 << (id & MOD_MASK)); + _sortedIncBuffer[i] = EcsMaskBit.FromID(inc[i]); + } + for (int i = 0; i < exc.Length; i++) + { + //int id = inc[i]; + //_sortedExcBuffer[i] = new EcsMaskBit(id >> DIV_SHIFT, 1 << (id & MOD_MASK)); + _sortedExcBuffer[i] = EcsMaskBit.FromID(exc[i]); + } + + EcsMaskBit[] incChunckMasks = mask.incChunckMasks; + EcsMaskBit[] excChunckMasks = mask.excChunckMasks; + + //_incChunckMasks = new ReadOnlySpan(_sortedIncBuffer, inc.Length); + //_excChunckMasks = new ReadOnlySpan(_sortedExcBuffer, exc.Length); + + int _sortedIncBufferLength = inc.Length; + int _sortedExcBufferLength = exc.Length; + + if (_sortedIncBufferLength > 1)//перенести этот чек в начала сортировки, для _incChunckMasks.Length == 1 сортировка не нужна + { + for (int i = 0, ii = 0; ii < incChunckMasks.Length; ii++) + { + EcsMaskBit bas = _sortedIncBuffer[i]; + int chankIndexX = bas.chankIndex; + int maskX = bas.mask; + + for (int j = i + 1; j < _sortedIncBufferLength; j++) + { + if (_sortedIncBuffer[j].chankIndex == chankIndexX) + { + maskX |= _sortedIncBuffer[j].mask; + } + } + incChunckMasks[ii] = new EcsMaskBit(chankIndexX, maskX); + while (++i < _sortedIncBufferLength && _sortedIncBuffer[i].chankIndex == chankIndexX) + { + + } + } + } + + if (_sortedExcBufferLength > 1)//перенести этот чек в начала сортировки, для _excChunckMasks.Length == 1 сортировка не нужна + { + for (int i = 0, ii = 0; ii < excChunckMasks.Length; ii++) + { + EcsMaskBit bas = _sortedExcBuffer[i]; + int chankIndexX = bas.chankIndex; + int maskX = bas.mask; + + for (int j = i + 1; j < _sortedExcBufferLength; j++) + { + if (_sortedExcBuffer[j].chankIndex == chankIndexX) + { + maskX |= _sortedExcBuffer[j].mask; + } + } + excChunckMasks[ii] = new EcsMaskBit(chankIndexX, maskX); + while (++i < _sortedExcBufferLength && _sortedExcBuffer[i].chankIndex == chankIndexX) + { + + } + } + } + + _incChunckMasks = incChunckMasks; + _excChunckMasks = excChunckMasks; + +#endif + #endregion + + #endregion + + #region MyRegion + //_inc = mask.inc; + //_exc = mask.exc; + //int[] inc = mask.inc; + //int[] exc = mask.exc; + //int[] counts = mask.World._poolComponentCounts; + //if (_inc.Length > 1) + //{ + // for (int i = 0; i < inc.Length; i++) + // { + // if (counts[inc[i]] <= 0) + // { + // _span = ReadOnlySpan.Empty.GetEnumerator(); + // goto skip1; + // } + // for (int j = 0; j < inc.Length - i - 1; j++) + // { + // if (counts[inc[i]] > counts[inc[j]]) + // { + // int tmp = inc[j]; + // inc[j] = inc[i]; + // inc[i] = tmp; + // } + // } + // } + //} + //skip1:; + //if(exc.Length > 1) + //{ + // for (int i = 0; i < exc.Length; i++) + // { + // if (counts[exc[i]] <= 0) + // { + // _exc = ReadOnlySpan.Empty; + // goto skip2; + // } + // for (int j = 0; j < exc.Length - i - 1; j++) + // { + // if (counts[exc[i]] < counts[exc[j]]) + // { + // int tmp = exc[j]; + // exc[j] = exc[i]; + // exc[i] = tmp; + // } + // } + // } + //} + //skip2:; + #endregion } public int Current { @@ -435,21 +738,49 @@ namespace DCFApixels.DragonECS while (_span.MoveNext()) { int e = _span.Current; - EcsMaskBit bit; - for (int i = 0, iMax = _incChunckMasks.Length; i < iMax; i++) + + foreach (var bit in _incChunckMasks) { - bit = _incChunckMasks[i]; if ((_entitiesComponentMasks[e][bit.chankIndex] & bit.mask) != bit.mask) goto skip; } - for (int i = 0, iMax = _excChunckMasks.Length; i < iMax; i++) + foreach (var bit in _excChunckMasks) { - bit = _excChunckMasks[i]; if ((_entitiesComponentMasks[e][bit.chankIndex] & bit.mask) > 0) goto skip; } + + //for (int i = 0, iMax = _incChunckMasks.Length; i < iMax; i++) + //{ + // var bit = _incChunckMasks[i]; + // if ((_entitiesComponentMasks[e][bit.chankIndex] & bit.mask) != bit.mask) + // goto skip; + //} + //for (int i = 0, iMax = _excChunckMasks.Length; i < iMax; i++) + //{ + // var bit = _excChunckMasks[i]; + // if ((_entitiesComponentMasks[e][bit.chankIndex] & bit.mask) > 0) + // goto skip; + //} return true; skip: continue; + + #region MyRegion + //for (int i = 0, iMax = _inc.Length; i < iMax; i++) + //{ + // bit = EcsMaskBit.FromID(_inc[i]); + // if ((_entitiesComponentMasks[e][bit.chankIndex] & bit.mask) != bit.mask) + // goto skip; + //} + //for (int i = 0, iMax = _exc.Length; i < iMax; i++) + //{ + // bit = EcsMaskBit.FromID(_exc[i]); + // if ((_entitiesComponentMasks[e][bit.chankIndex] & bit.mask) > 0) + // goto skip; + //} + //return true; + //skip: continue; + #endregion } return false; } diff --git a/src/EcsWorld.cs b/src/EcsWorld.cs index 43ceec2..6561464 100644 --- a/src/EcsWorld.cs +++ b/src/EcsWorld.cs @@ -66,7 +66,9 @@ namespace DCFApixels.DragonECS _entityDispenser = new IntDispenser(0); _pools = new IEcsPoolImplementation[POOLS_CAPACITY]; - //_poolComponentCounts = new int[POOLS_CAPACITY]; + _poolComponentCounts = new int[POOLS_CAPACITY]; + //_sortedPoolIds = new int[POOLS_CAPACITY]; + //_sortedPoolIdsMapping = new int[POOLS_CAPACITY]; ArrayUtility.Fill(_pools, _nullPool); _gens = new short[_entitesCapacity]; @@ -74,7 +76,6 @@ namespace DCFApixels.DragonECS ArrayUtility.Fill(_gens, DEATH_GEN_BIT); _delEntBufferCount = 0; - //_delEntBuffer = new int[_entitesCapacity >> DEL_ENT_BUFFER_SIZE_OFFSET]; _delEntBuffer = new int[_entitesCapacity]; _entitiesComponentMasks = new int[_entitesCapacity][]; for (int i = 0; i < _entitesCapacity; i++) diff --git a/src/EcsWorld.pools.cs b/src/EcsWorld.pools.cs index f712602..5c61b60 100644 --- a/src/EcsWorld.pools.cs +++ b/src/EcsWorld.pools.cs @@ -12,7 +12,9 @@ namespace DCFApixels.DragonECS private SparseArray _componentIds = new SparseArray(); private int _poolsCount; internal IEcsPoolImplementation[] _pools; - //internal int[] _poolComponentCounts; + //private int[] _sortedPoolIds; + //private int[] _sortedPoolIdsMapping; + internal int[] _poolComponentCounts; private static EcsNullPool _nullPool = EcsNullPool.instance; @@ -26,6 +28,13 @@ namespace DCFApixels.DragonECS #region Getters + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public TPool TestGetPool() where TPool : IEcsPoolImplementation, new() + { + return Get>().instance; + } + + #if UNITY_2020_3_OR_NEWER [UnityEngine.Scripting.Preserve] #endif @@ -94,7 +103,9 @@ namespace DCFApixels.DragonECS { int oldCapacity = _pools.Length; Array.Resize(ref _pools, _pools.Length << 1); - //Array.Resize(ref _poolComponentCounts, _pools.Length); + Array.Resize(ref _poolComponentCounts, _pools.Length); + //Array.Resize(ref _sortedPoolIds, _pools.Length); + //Array.Resize(ref _sortedPoolIdsMapping, _pools.Length); ArrayUtility.Fill(_pools, _nullPool, oldCapacity, oldCapacity - _pools.Length); for (int i = 0; i < _entitesCapacity; i++) @@ -111,18 +122,19 @@ namespace DCFApixels.DragonECS } #endregion + #region Pools mediation [MethodImpl(MethodImplOptions.AggressiveInlining)] private void RegisterEntityComponent(int entityID, int componentTypeID, EcsMaskBit maskBit) { - //_poolComponentCounts[componentTypeID]++; + _poolComponentCounts[componentTypeID]++; _componentCounts[entityID]++; _entitiesComponentMasks[entityID][maskBit.chankIndex] |= maskBit.mask; } [MethodImpl(MethodImplOptions.AggressiveInlining)] private void UnregisterEntityComponent(int entityID, int componentTypeID, EcsMaskBit maskBit) { - //_poolComponentCounts[componentTypeID]--; + _poolComponentCounts[componentTypeID]--; var count = --_componentCounts[entityID]; _entitiesComponentMasks[entityID][maskBit.chankIndex] &= ~maskBit.mask; From b34f8add008ccc472c38e3e2d6c24a9952a7780a Mon Sep 17 00:00:00 2001 From: Mikhail <99481254+DCFApixels@users.noreply.github.com> Date: Sun, 7 Jan 2024 18:52:54 +0800 Subject: [PATCH 04/11] update sorting & masks --- src/Collections/EcsGroup.cs | 2 +- src/Collections/EcsSpan.cs | 2 +- src/EcsAspect.cs | 725 ++++++++++++---------------------- src/EcsMask.cs | 346 ++++++++++++++++ src/EcsWorld.pools.cs | 12 +- src/Pools/EcsHybridPool.cs | 4 +- src/Pools/EcsPool.cs | 4 +- src/Pools/EcsTagPool.cs | 4 +- src/Utils/ArraySortHalperX.cs | 179 +++++++++ src/Utils/ArrayUtility.cs | 82 +++- 10 files changed, 873 insertions(+), 487 deletions(-) create mode 100644 src/EcsMask.cs create mode 100644 src/Utils/ArraySortHalperX.cs diff --git a/src/Collections/EcsGroup.cs b/src/Collections/EcsGroup.cs index d95d92d..7d8d441 100644 --- a/src/Collections/EcsGroup.cs +++ b/src/Collections/EcsGroup.cs @@ -721,7 +721,7 @@ namespace DCFApixels.DragonECS #region Other public override string ToString() { - return EntitiesCollectionUtility.AutoToString(_dense.Skip(1).Take(_count), "group"); + return CollectionUtility.EntitiesToString(_dense.Skip(1).Take(_count), "group"); //return $"group({_count}) {{{string.Join(", ", _dense.Skip(1).Take(_count).OrderBy(o => o))}}}"; } [MethodImpl(MethodImplOptions.AggressiveInlining)] diff --git a/src/Collections/EcsSpan.cs b/src/Collections/EcsSpan.cs index 6a943df..be3e2e7 100644 --- a/src/Collections/EcsSpan.cs +++ b/src/Collections/EcsSpan.cs @@ -122,7 +122,7 @@ namespace DCFApixels.DragonECS #pragma warning restore CS0809 // Устаревший член переопределяет неустаревший член public override string ToString() { - return EntitiesCollectionUtility.AutoToString(_values.ToArray(), "span"); + return CollectionUtility.EntitiesToString(_values.ToArray(), "span"); //return $"span({_values.Length}) {{{string.Join(", ", _values.ToArray().OrderBy(o => o))}}}"; } diff --git a/src/EcsAspect.cs b/src/EcsAspect.cs index 1db879b..924a081 100644 --- a/src/EcsAspect.cs +++ b/src/EcsAspect.cs @@ -1,32 +1,32 @@ -//#define sort_1 -#define lockSwapCheck -// Зеленый свет, тесты показали идентичную скорость или даже прирост - -using DCFApixels.DragonECS.Internal; +using DCFApixels.DragonECS.Internal; using DCFApixels.DragonECS.Utils; using System; using System.Collections.Generic; -using System.Diagnostics; using System.Linq; using System.Reflection; using System.Runtime.CompilerServices; -using System.Text; namespace DCFApixels.DragonECS { public abstract class EcsAspect { - internal EcsWorld source; - internal EcsMask mask; + internal EcsWorld _source; + internal EcsMask _mask; private bool _isInit; + + internal UnsafeArray _sortIncBuffer; + internal UnsafeArray _sortExcBuffer; + internal UnsafeArray _sortIncChunckBuffer; + internal UnsafeArray _sortExcChunckBuffer; + #region Properties - public EcsMask Mask => mask; - public EcsWorld World => source; + public EcsMask Mask => _mask; + public EcsWorld World => _source; public bool IsInit => _isInit; #endregion #region Methods - public bool IsMatches(int entityID) => source.IsMatchesMask(mask, entityID); + public bool IsMatches(int entityID) => _source.IsMatchesMask(_mask, entityID); #endregion #region Builder @@ -47,7 +47,7 @@ namespace DCFApixels.DragonECS _inc = new HashSet(); _exc = new HashSet(); } - internal static TAspect Build(EcsWorld world) where TAspect : EcsAspect + internal static unsafe TAspect Build(EcsWorld world) where TAspect : EcsAspect { Builder builder = new Builder(world); Type aspectType = typeof(TAspect); @@ -62,9 +62,33 @@ namespace DCFApixels.DragonECS newAspect = (EcsAspect)Activator.CreateInstance(typeof(TAspect)); newAspect.Init(builder); } - newAspect.source = world; - builder.End(out newAspect.mask); + newAspect._source = world; + builder.End(out newAspect._mask); newAspect._isInit = true; + + newAspect._sortIncBuffer = new UnsafeArray(newAspect._mask.inc.Length, true); + newAspect._sortExcBuffer = new UnsafeArray(newAspect._mask.exc.Length, true); + newAspect._sortIncChunckBuffer = new UnsafeArray(newAspect._mask.incChunckMasks.Length, true); + newAspect._sortExcChunckBuffer = new UnsafeArray(newAspect._mask.excChunckMasks.Length, true); + + for (int i = 0; i < newAspect._sortIncBuffer.Length; i++) + { + newAspect._sortIncBuffer.ptr[i] = newAspect._mask.inc[i]; + } + for (int i = 0; i < newAspect._sortExcBuffer.Length; i++) + { + newAspect._sortExcBuffer.ptr[i] = newAspect._mask.exc[i]; + } + + for (int i = 0; i < newAspect._sortIncChunckBuffer.Length; i++) + { + newAspect._sortIncChunckBuffer.ptr[i] = newAspect._mask.incChunckMasks[i]; + } + for (int i = 0; i < newAspect._sortExcChunckBuffer.Length; i++) + { + newAspect._sortExcChunckBuffer.ptr[i] = newAspect._mask.excChunckMasks[i]; + } + return (TAspect)newAspect; } @@ -126,7 +150,7 @@ namespace DCFApixels.DragonECS _combined.Sort((a, b) => a.order - b.order); foreach (var item in _combined) { - EcsMask submask = item.aspect.mask; + EcsMask submask = item.aspect._mask; maskInc.ExceptWith(submask.exc);//удаляю конфликтующие ограничения maskExc.ExceptWith(submask.inc);//удаляю конфликтующие ограничения maskInc.UnionWith(submask.inc); @@ -146,26 +170,26 @@ namespace DCFApixels.DragonECS Dictionary r = new Dictionary(); foreach (var id in maskInc) { - var bit = EcsMaskBit.FromID(id); + var bit = EcsMaskChunck.FromID(id); if (!r.TryAdd(bit.chankIndex, bit.mask)) r[bit.chankIndex] = r[bit.chankIndex] | bit.mask; } - EcsMaskBit[] incMasks = r.Select(o => new EcsMaskBit(o.Key, o.Value)).ToArray(); + EcsMaskChunck[] incMasks = r.Select(o => new EcsMaskChunck(o.Key, o.Value)).ToArray(); r.Clear(); foreach (var id in maskExc) { - var bit = EcsMaskBit.FromID(id); + var bit = EcsMaskChunck.FromID(id); if (!r.TryAdd(bit.chankIndex, bit.mask)) r[bit.chankIndex] = r[bit.chankIndex] | bit.mask; } - EcsMaskBit[] excMasks = r.Select(o => new EcsMaskBit(o.Key, o.Value)).ToArray(); + EcsMaskChunck[] excMasks = r.Select(o => new EcsMaskChunck(o.Key, o.Value)).ToArray(); var inc = maskInc.ToArray(); Array.Sort(inc); var exc = maskExc.ToArray(); Array.Sort(exc); - mask = new EcsMask(_world.id, inc, exc, incMasks, excMasks); + mask = new EcsMask(0, _world.id, inc, exc, incMasks, excMasks); _world = null; _inc = null; _exc = null; @@ -187,10 +211,20 @@ namespace DCFApixels.DragonECS } #endregion + #region Destructor + unsafe ~EcsAspect() + { + _sortIncBuffer.Dispose(); + _sortExcBuffer.Dispose(); + _sortIncChunckBuffer.Dispose(); + _sortExcChunckBuffer.Dispose(); + } + #endregion + #region Iterator public EcsAspectIterator GetIterator() { - return new EcsAspectIterator(this, source.Entities); + return new EcsAspectIterator(this, _source.Entities); } public EcsAspectIterator GetIteratorFor(EcsSpan span) { @@ -198,6 +232,7 @@ namespace DCFApixels.DragonECS } #endregion + #region Combined private readonly struct Combined { public readonly EcsAspect aspect; @@ -208,6 +243,7 @@ namespace DCFApixels.DragonECS this.order = order; } } + #endregion } #region BuilderBase @@ -219,168 +255,19 @@ namespace DCFApixels.DragonECS } #endregion - #region Mask - [DebuggerTypeProxy(typeof(DebuggerProxy))] - public readonly struct EcsMaskBit - { - private const int BITS = 32; - private const int DIV_SHIFT = 5; - private const int MOD_MASK = BITS - 1; - - public readonly int chankIndex; - public readonly int mask; - public EcsMaskBit(int chankIndex, int mask) - { - this.chankIndex = chankIndex; - this.mask = mask; - } - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static EcsMaskBit FromID(int id) - { - return new EcsMaskBit(id >> DIV_SHIFT, 1 << (id & MOD_MASK)); //аналогично new EcsMaskBit(id / BITS, 1 << (id % BITS)) но быстрее - } - public override string ToString() - { - return $"mask({chankIndex}, {mask}, {BitsUtility.CountBits(mask)})"; - } - internal class DebuggerProxy - { - public int chunk; - public uint mask; - public int[] values = Array.Empty(); - public string bits; - public DebuggerProxy(EcsMaskBit maskbits) - { - chunk = maskbits.chankIndex; - mask = (uint)maskbits.mask; - BitsUtility.GetBitNumbersNoAlloc(mask, ref values); - for (int i = 0; i < values.Length; i++) - { - values[i] += (chunk) << 5; - } - bits = BitsUtility.ToBitsString(mask, '_', 8); - } - } - } - - [DebuggerTypeProxy(typeof(DebuggerProxy))] - public sealed class EcsMask - { - internal readonly int worldID; - internal readonly EcsMaskBit[] incChunckMasks; - internal readonly EcsMaskBit[] excChunckMasks; - internal readonly int[] inc; - internal readonly int[] exc; - public int WorldID => worldID; - public EcsWorld World => EcsWorld.GetWorld(worldID); - /// Including constraints - public ReadOnlySpan Inc => inc; - /// Excluding constraints - public ReadOnlySpan Exc => exc; - internal EcsMask(int worldID, int[] inc, int[] exc, EcsMaskBit[] incChunckMasks, EcsMaskBit[] excChunckMasks) - { -#if DEBUG - CheckConstraints(inc, exc); -#endif - this.inc = inc; - this.exc = exc; - this.worldID = worldID; - this.incChunckMasks = incChunckMasks; - this.excChunckMasks = excChunckMasks; - } - - #region Object - public override string ToString() => CreateLogString(worldID, inc, exc); - #endregion - - #region Debug utils -#if DEBUG - private static HashSet _dummyHashSet = new HashSet(); - private void CheckConstraints(int[] inc, int[] exc) - { - lock (_dummyHashSet) - { - if (CheckRepeats(inc)) throw new EcsFrameworkException("The values in the Include constraints are repeated."); - if (CheckRepeats(exc)) throw new EcsFrameworkException("The values in the Exclude constraints are repeated."); - _dummyHashSet.Clear(); - _dummyHashSet.UnionWith(inc); - if (_dummyHashSet.Overlaps(exc)) throw new EcsFrameworkException("Conflicting Include and Exclude constraints."); - } - } - private bool CheckRepeats(int[] array) - { - _dummyHashSet.Clear(); - foreach (var item in array) - { - if (_dummyHashSet.Contains(item)) return true; - _dummyHashSet.Add(item); - } - return false; - } -#endif - private static string CreateLogString(int worldID, int[] inc, int[] exc) - { -#if (DEBUG && !DISABLE_DEBUG) - string converter(int o) => EcsDebugUtility.GetGenericTypeName(EcsWorld.GetWorld(worldID).AllPools[o].ComponentType, 1); - return $"Inc({string.Join(", ", inc.Select(converter))}) Exc({string.Join(", ", exc.Select(converter))})"; -#else - return $"Inc({string.Join(", ", inc)}) Exc({string.Join(", ", exc)})"; // Release optimization -#endif - } - internal class DebuggerProxy - { - public readonly EcsWorld world; - public readonly int worldID; - public readonly EcsMaskBit[] includedChunkMasks; - public readonly EcsMaskBit[] excludedChunkMasks; - public readonly int[] included; - public readonly int[] excluded; - public readonly Type[] includedTypes; - public readonly Type[] excludedTypes; - - public DebuggerProxy(EcsMask mask) - { - world = EcsWorld.GetWorld(mask.worldID); - worldID = mask.worldID; - includedChunkMasks = mask.incChunckMasks; - excludedChunkMasks = mask.excChunckMasks; - included = mask.inc; - excluded = mask.exc; - Type converter(int o) => world.GetComponentType(o); - includedTypes = included.Select(converter).ToArray(); - excludedTypes = excluded.Select(converter).ToArray(); - } - public override string ToString() => CreateLogString(worldID, included, excluded); - } - #endregion - } - #endregion - #region Iterator public ref struct EcsAspectIterator { public readonly int worldID; - public readonly EcsMask mask; + public readonly EcsAspect aspect; private EcsSpan _span; - private Enumerator _enumerator; public EcsAspectIterator(EcsAspect aspect, EcsSpan span) { worldID = aspect.World.id; - mask = aspect.mask; _span = span; - _enumerator = default; + this.aspect = aspect; } - - public int Current - { - [MethodImpl(MethodImplOptions.AggressiveInlining)] - get => _enumerator.Current; - } - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public void Begin() => _enumerator = GetEnumerator(); - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public bool Next() => _enumerator.MoveNext(); public void CopyTo(EcsGroup group) { group.Clear(); @@ -416,315 +303,237 @@ namespace DCFApixels.DragonECS #region object public override string ToString() { - StringBuilder result = new StringBuilder(); + List ints = new List(); foreach (var e in this) { - result.Append(e); - result.Append(", "); + ints.Add(e); } - return result.ToString(); + return CollectionUtility.EntitiesToString(ints, "it"); } #endregion #region Enumerator [MethodImpl(MethodImplOptions.AggressiveInlining)] - public Enumerator GetEnumerator() => new Enumerator(_span, mask); - + public Enumerator GetEnumerator() => new Enumerator(_span, aspect); public unsafe ref struct Enumerator { + private readonly struct IncCountComparer : IComparerX + { + public readonly int[] counts; + public IncCountComparer(int[] counts) + { + this.counts = counts; + } + public int Compare(int a, int b) + { + return counts[a] - counts[b]; + } + } + private readonly struct ExcCountComparer : IComparerX + { + public readonly int[] counts; + public ExcCountComparer(int[] counts) + { + this.counts = counts; + } + public int Compare(int a, int b) + { + return counts[b] - counts[a]; + } + } private ReadOnlySpan.Enumerator _span; -#if sort_1 - private readonly EcsMaskBit[] _incChunckMasks; - private readonly EcsMaskBit[] _excChunckMasks; -#else - private readonly EcsMaskBit[] _incChunckMasks; - private readonly EcsMaskBit[] _excChunckMasks; -#endif private readonly int[][] _entitiesComponentMasks; + private static EcsMaskChunck* _preSortedIncBuffer; + private static EcsMaskChunck* _preSortedExcBuffer; + private UnsafeArray _sortIncChunckBuffer; + private UnsafeArray _sortExcChunckBuffer; - private static EcsMaskBit* _sortedIncBuffer; - private static EcsMaskBit* _sortedExcBuffer; - private static SparseArray _sp; - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public unsafe Enumerator(EcsSpan span, EcsMask mask) + private EcsAspect aspect; + + public unsafe Enumerator(EcsSpan span, EcsAspect aspect) { + this.aspect = aspect; _span = span.GetEnumerator(); -#if sort_1 - _incChunckMasks = mask.incChunckMasks; - _excChunckMasks = mask.excChunckMasks; -#endif _entitiesComponentMasks = span.World._entitiesComponentMasks; + EcsMask mask = aspect.Mask; + + UnsafeArray _sortIncBuffer = aspect._sortIncBuffer; + UnsafeArray _sortExcBuffer = aspect._sortExcBuffer; + _sortIncChunckBuffer = aspect._sortIncChunckBuffer; + _sortExcChunckBuffer = aspect._sortExcChunckBuffer; - #region MyRegion - #region Trash -#if !sort_1 - int[] inc = mask.inc; - int[] exc = mask.exc; int[] counts = mask.World._poolComponentCounts; - if (inc.Length > 1) - { - //if (inc.Length == 2) - //{ - // if (counts[inc[0]] > counts[inc[1]]) - // { - // int tmp = inc[0]; - // inc[0] = inc[1]; - // inc[1] = tmp; - // } - // //... - //} - //else - { - //for (int i = 0; i < inc.Length; i++) - //{ - // int counti = counts[inc[i]]; - // if (counti <= 0) - // { - // _span = ReadOnlySpan.Empty.GetEnumerator(); - // goto skip1; - // } - // for (int j = inc.Length - 1; j >= i; j--) - // { - // if (counts[inc[j]] < counti) - // { - // int tmp = inc[j]; - // inc[j] = inc[i]; - // inc[i] = tmp; - // } - // } - //} - //... - for (int i = 0, n = inc.Length; i < n - 1; i++) - { - //int counti = counts[inc[i]]; - //if (counti <= 0) - //{ - // _span = ReadOnlySpan.Empty.GetEnumerator(); - // goto skip1; - //} - bool noSwaped = true; - for (int j = 0; j < n - i - 1; ) - { - ref int j0 = ref inc[j++]; - if (counts[j0] > counts[inc[j]]) - { - int tmp = inc[j]; - inc[j] = j0; - j0 = tmp; - noSwaped = false; - } - } -#if !lockSwapCheck - if (noSwaped) - break; -#endif - } - } - } - skip1:; - if (exc.Length > 1) - { - //if (exc.Length == 2) - //{ - // if (counts[exc[0]] < counts[exc[1]]) - // { - // int tmp = exc[0]; - // exc[0] = inc[1]; - // exc[1] = tmp; - // } - // //... - //} - //else - { - //for (int i = 0; i < exc.Length; i++) - //{ - // int counti = counts[inc[i]]; - // if (counti <= 0) - // { - // _excChunckMasks = ReadOnlySpan.Empty; - // goto skip2; - // } - // for (int j = exc.Length - 1; j >= i; j--) - // { - // if (counts[exc[j]] > counti) - // { - // int tmp = exc[j]; - // exc[j] = exc[i]; - // exc[i] = tmp; - // } - // } - //} - //... + IncCountComparer incComparer = new IncCountComparer(counts); + ExcCountComparer excComparer = new ExcCountComparer(counts); - for (int i = 0, n = exc.Length; i < n - 1; i++) - { - //int counti = counts[inc[i]]; - //if (counti <= 0) - //{ - // _excChunckMasks = ReadOnlySpan.Empty; - // goto skip2; - //} - bool noSwaped = true; - for (int j = 0; j < n - i - 1;) - { - ref int j0 = ref exc[j++]; - if (counts[j0] < counts[exc[j]]) - { - int tmp = exc[j]; - exc[j] = j0; - j0 = tmp; - noSwaped = false; - } - } -#if !lockSwapCheck - if (noSwaped) - break; -#endif - } - } - } - skip2:; + #region Sort + UnsafeArraySortHalperX.InsertionSort(_sortIncBuffer.ptr, _sortIncBuffer.Length, ref incComparer); + UnsafeArraySortHalperX.InsertionSort(_sortExcBuffer.ptr, _sortExcBuffer.Length, ref excComparer); - if (_sortedIncBuffer == null) - { - _sortedIncBuffer = UnmanagedArrayUtility.New(256); - _sortedExcBuffer = UnmanagedArrayUtility.New(256); - _sp = new SparseArray(32); - } - - _sp.Clear(); - for (int i = 0, ii = 0; i < inc.Length; i++) - { - //int id = inc[i]; - //_sortedIncBuffer[i] = new EcsMaskBit(id >> DIV_SHIFT, 1 << (id & MOD_MASK)); - _sortedIncBuffer[i] = EcsMaskBit.FromID(inc[i]); - } - for (int i = 0; i < exc.Length; i++) - { - //int id = inc[i]; - //_sortedExcBuffer[i] = new EcsMaskBit(id >> DIV_SHIFT, 1 << (id & MOD_MASK)); - _sortedExcBuffer[i] = EcsMaskBit.FromID(exc[i]); - } - - EcsMaskBit[] incChunckMasks = mask.incChunckMasks; - EcsMaskBit[] excChunckMasks = mask.excChunckMasks; - - //_incChunckMasks = new ReadOnlySpan(_sortedIncBuffer, inc.Length); - //_excChunckMasks = new ReadOnlySpan(_sortedExcBuffer, exc.Length); - - - int _sortedIncBufferLength = inc.Length; - int _sortedExcBufferLength = exc.Length; - - if (_sortedIncBufferLength > 1)//перенести этот чек в начала сортировки, для _incChunckMasks.Length == 1 сортировка не нужна - { - for (int i = 0, ii = 0; ii < incChunckMasks.Length; ii++) - { - EcsMaskBit bas = _sortedIncBuffer[i]; - int chankIndexX = bas.chankIndex; - int maskX = bas.mask; - - for (int j = i + 1; j < _sortedIncBufferLength; j++) - { - if (_sortedIncBuffer[j].chankIndex == chankIndexX) - { - maskX |= _sortedIncBuffer[j].mask; - } - } - incChunckMasks[ii] = new EcsMaskBit(chankIndexX, maskX); - while (++i < _sortedIncBufferLength && _sortedIncBuffer[i].chankIndex == chankIndexX) - { - - } - } - } - - if (_sortedExcBufferLength > 1)//перенести этот чек в начала сортировки, для _excChunckMasks.Length == 1 сортировка не нужна - { - for (int i = 0, ii = 0; ii < excChunckMasks.Length; ii++) - { - EcsMaskBit bas = _sortedExcBuffer[i]; - int chankIndexX = bas.chankIndex; - int maskX = bas.mask; - - for (int j = i + 1; j < _sortedExcBufferLength; j++) - { - if (_sortedExcBuffer[j].chankIndex == chankIndexX) - { - maskX |= _sortedExcBuffer[j].mask; - } - } - excChunckMasks[ii] = new EcsMaskBit(chankIndexX, maskX); - while (++i < _sortedExcBufferLength && _sortedExcBuffer[i].chankIndex == chankIndexX) - { - - } - } - } - - _incChunckMasks = incChunckMasks; - _excChunckMasks = excChunckMasks; - -#endif - #endregion - - #endregion - - #region MyRegion - //_inc = mask.inc; - //_exc = mask.exc; - //int[] inc = mask.inc; - //int[] exc = mask.exc; - //int[] counts = mask.World._poolComponentCounts; - //if (_inc.Length > 1) + //if (_sortIncBuffer.Length > 1) //{ - // for (int i = 0; i < inc.Length; i++) + // //if (_sortIncBufferLength == 2) + // //{ + // // if (counts[_sortIncBuffer[0]] > counts[_sortIncBuffer[1]]) + // // { + // // int tmp = _sortIncBuffer[0]; + // // _sortIncBuffer[0] = _sortIncBuffer[1]; + // // _sortIncBuffer[1] = tmp; + // // } + // // //... + // //} + // //else // { - // if (counts[inc[i]] <= 0) + // for (int i = 0, n = _sortIncBuffer.Length - 1; i < n; i++) // { - // _span = ReadOnlySpan.Empty.GetEnumerator(); - // goto skip1; - // } - // for (int j = 0; j < inc.Length - i - 1; j++) - // { - // if (counts[inc[i]] > counts[inc[j]]) + // //int counti = counts[_sortIncBuffer[i]]; + // //if (counti <= 0) + // //{ + // // _span = ReadOnlySpan.Empty.GetEnumerator(); + // // goto skip1; + // //} + // bool noSwaped = true; + // for (int j = 0; j < n - i; ) // { - // int tmp = inc[j]; - // inc[j] = inc[i]; - // inc[i] = tmp; + // ref int j0 = ref _sortIncBuffer.ptr[j++]; + // if (counts[j0] > counts[_sortIncBuffer.ptr[j]]) + // { + // int tmp = _sortIncBuffer.ptr[j]; + // _sortIncBuffer.ptr[j] = j0; + // j0 = tmp; + // noSwaped = false; + // } // } + // if (noSwaped) + // break; // } // } //} //skip1:; - //if(exc.Length > 1) + //if (_sortExcBuffer.Length > 1) //{ - // for (int i = 0; i < exc.Length; i++) + // //if (_sortExcBufferLength == 2) + // //{ + // // if (counts[_sortExcBuffer[0]] < counts[_sortExcBuffer[1]]) + // // { + // // int tmp = _sortExcBuffer[0]; + // // _sortExcBuffer[0] = _sortExcBuffer[1]; + // // _sortExcBuffer[1] = tmp; + // // } + // // //... + // //} + // //else // { - // if (counts[exc[i]] <= 0) + // for (int i = 0, n = _sortExcBuffer.Length - 1; i < n; i++) // { - // _exc = ReadOnlySpan.Empty; - // goto skip2; - // } - // for (int j = 0; j < exc.Length - i - 1; j++) - // { - // if (counts[exc[i]] < counts[exc[j]]) + // //int counti = counts[_sortExcBuffer[i]]; + // //if (counti <= 0) + // //{ + // // _excChunckMasks = ReadOnlySpan.Empty; + // // goto skip2; + // //} + // bool noSwaped = true; + // for (int j = 0; j < n - i;) // { - // int tmp = exc[j]; - // exc[j] = exc[i]; - // exc[i] = tmp; + // ref int j0 = ref _sortExcBuffer.ptr[j++]; + // if (counts[j0] < counts[_sortExcBuffer.ptr[j]]) + // { + // int tmp = _sortExcBuffer.ptr[j]; + // _sortExcBuffer.ptr[j] = j0; + // j0 = tmp; + // noSwaped = false; + // } // } + // if (noSwaped) + // break; // } // } //} //skip2:; + + + + if (_preSortedIncBuffer == null) + { + _preSortedIncBuffer = UnmanagedArrayUtility.New(256); + _preSortedExcBuffer = UnmanagedArrayUtility.New(256); + } + + + for (int i = 0; i < _sortIncBuffer.Length; i++) + { + _preSortedIncBuffer[i] = EcsMaskChunck.FromID(_sortIncBuffer.ptr[i]); + } + for (int i = 0; i < _sortExcBuffer.Length; i++) + { + _preSortedExcBuffer[i] = EcsMaskChunck.FromID(_sortExcBuffer.ptr[i]); + } + + //int _sortedIncBufferLength = mask.inc.Length; + //int _sortedExcBufferLength = mask.exc.Length; + + //if (_sortIncChunckBuffer.Length > 1)//перенести этот чек в начала сортировки, для _incChunckMasks.Length == 1 сортировка не нужна + if (_sortIncBuffer.Length > 1) + { + for (int i = 0, ii = 0; ii < _sortIncChunckBuffer.Length; ii++) + { + EcsMaskChunck bas = _preSortedIncBuffer[i]; + int chankIndexX = bas.chankIndex; + int maskX = bas.mask; + + for (int j = i + 1; j < _sortIncBuffer.Length; j++) + { + if (_preSortedIncBuffer[j].chankIndex == chankIndexX) + { + maskX |= _preSortedIncBuffer[j].mask; + } + } + _sortIncChunckBuffer.ptr[ii] = new EcsMaskChunck(chankIndexX, maskX); + while (++i < _sortIncBuffer.Length && _preSortedIncBuffer[i].chankIndex == chankIndexX) + { + // skip + } + } + } + else + { + _sortIncChunckBuffer.ptr[0] = _preSortedIncBuffer[0]; + } + + //if (_sortExcChunckBuffer.Length > 1)//перенести этот чек в начала сортировки, для _excChunckMasks.Length == 1 сортировка не нужна + if (_sortExcBuffer.Length > 1) + { + for (int i = 0, ii = 0; ii < _sortExcChunckBuffer.Length; ii++) + { + EcsMaskChunck bas = _preSortedExcBuffer[i]; + int chankIndexX = bas.chankIndex; + int maskX = bas.mask; + + for (int j = i + 1; j < _sortExcBuffer.Length; j++) + { + if (_preSortedExcBuffer[j].chankIndex == chankIndexX) + { + maskX |= _preSortedExcBuffer[j].mask; + } + } + _sortExcChunckBuffer.ptr[ii] = new EcsMaskChunck(chankIndexX, maskX); + while (++i < _sortExcBuffer.Length && _preSortedExcBuffer[i].chankIndex == chankIndexX) + { + // skip + } + } + } + else + { + _sortExcChunckBuffer.ptr[0] = _preSortedExcBuffer[0]; + } #endregion } public int Current @@ -732,55 +541,31 @@ namespace DCFApixels.DragonECS [MethodImpl(MethodImplOptions.AggressiveInlining)] get => _span.Current; } + public entlong CurrentLong + { + [MethodImpl(MethodImplOptions.AggressiveInlining)] + get => aspect.World.GetEntityLong(_span.Current); + } [MethodImpl(MethodImplOptions.AggressiveInlining)] public bool MoveNext() { while (_span.MoveNext()) { int e = _span.Current; - - foreach (var bit in _incChunckMasks) + for (int i = 0; i < _sortIncChunckBuffer.Length; i++) { + var bit = _sortIncChunckBuffer.ptr[i]; if ((_entitiesComponentMasks[e][bit.chankIndex] & bit.mask) != bit.mask) goto skip; } - foreach (var bit in _excChunckMasks) + for (int i = 0; i < _sortExcChunckBuffer.Length; i++) { + var bit = _sortExcChunckBuffer.ptr[i]; if ((_entitiesComponentMasks[e][bit.chankIndex] & bit.mask) > 0) goto skip; } - - //for (int i = 0, iMax = _incChunckMasks.Length; i < iMax; i++) - //{ - // var bit = _incChunckMasks[i]; - // if ((_entitiesComponentMasks[e][bit.chankIndex] & bit.mask) != bit.mask) - // goto skip; - //} - //for (int i = 0, iMax = _excChunckMasks.Length; i < iMax; i++) - //{ - // var bit = _excChunckMasks[i]; - // if ((_entitiesComponentMasks[e][bit.chankIndex] & bit.mask) > 0) - // goto skip; - //} return true; skip: continue; - - #region MyRegion - //for (int i = 0, iMax = _inc.Length; i < iMax; i++) - //{ - // bit = EcsMaskBit.FromID(_inc[i]); - // if ((_entitiesComponentMasks[e][bit.chankIndex] & bit.mask) != bit.mask) - // goto skip; - //} - //for (int i = 0, iMax = _exc.Length; i < iMax; i++) - //{ - // bit = EcsMaskBit.FromID(_exc[i]); - // if ((_entitiesComponentMasks[e][bit.chankIndex] & bit.mask) > 0) - // goto skip; - //} - //return true; - //skip: continue; - #endregion } return false; } diff --git a/src/EcsMask.cs b/src/EcsMask.cs new file mode 100644 index 0000000..f3da598 --- /dev/null +++ b/src/EcsMask.cs @@ -0,0 +1,346 @@ +using DCFApixels.DragonECS.Internal; +using System; +using System.Collections.Generic; +using System.Diagnostics; +using System.Linq; +using System.Runtime.CompilerServices; +using static Leopotam.EcsLite.EcsWorld; + +namespace DCFApixels.DragonECS +{ + public abstract partial class EcsWorld + { + private Dictionary _masks = new Dictionary(256); + internal EcsMask GetMask_Internal(EcsMask.BuilderMaskKey maskKey) + { + if(!_masks.TryGetValue(maskKey, out EcsMask result)) + { + int[] combinedInc = maskKey.inc; + int[] combinedExc = maskKey.exc; + Dictionary r = new Dictionary(); + foreach (var id in combinedInc) + { + var bit = EcsMaskChunck.FromID(id); + if (!r.TryAdd(bit.chankIndex, bit.mask)) + r[bit.chankIndex] = r[bit.chankIndex] | bit.mask; + } + EcsMaskChunck[] incMasks = r.Select(o => new EcsMaskChunck(o.Key, o.Value)).ToArray(); + r.Clear(); + foreach (var id in combinedExc) + { + var bit = EcsMaskChunck.FromID(id); + if (!r.TryAdd(bit.chankIndex, bit.mask)) + r[bit.chankIndex] = r[bit.chankIndex] | bit.mask; + } + EcsMaskChunck[] excMasks = r.Select(o => new EcsMaskChunck(o.Key, o.Value)).ToArray(); + + result = new EcsMask(_masks.Count, id, combinedInc, combinedExc, incMasks, excMasks); + _masks.Add(maskKey, result); + } + return result; + } + } + + + + [DebuggerTypeProxy(typeof(DebuggerProxy))] + public sealed class EcsMask + { + internal readonly int id; + internal readonly int worldID; + internal readonly EcsMaskChunck[] incChunckMasks; + internal readonly EcsMaskChunck[] excChunckMasks; + internal readonly int[] inc; + internal readonly int[] exc; + public int WorldID => worldID; + public EcsWorld World => EcsWorld.GetWorld(worldID); + /// Including constraints + public ReadOnlySpan Inc => inc; + /// Excluding constraints + public ReadOnlySpan Exc => exc; + internal EcsMask(int id, int worldID, int[] inc, int[] exc, EcsMaskChunck[] incChunckMasks, EcsMaskChunck[] excChunckMasks) + { +#if DEBUG + CheckConstraints(inc, exc); +#endif + this.id = id; + this.inc = inc; + this.exc = exc; + this.worldID = worldID; + this.incChunckMasks = incChunckMasks; + this.excChunckMasks = excChunckMasks; + } + + #region Object + public override string ToString() => CreateLogString(worldID, inc, exc); + #endregion + + #region Debug utils +#if DEBUG + private static HashSet _dummyHashSet = new HashSet(); + private void CheckConstraints(int[] inc, int[] exc) + { + lock (_dummyHashSet) + { + if (CheckRepeats(inc)) throw new EcsFrameworkException("The values in the Include constraints are repeated."); + if (CheckRepeats(exc)) throw new EcsFrameworkException("The values in the Exclude constraints are repeated."); + _dummyHashSet.Clear(); + _dummyHashSet.UnionWith(inc); + if (_dummyHashSet.Overlaps(exc)) throw new EcsFrameworkException("Conflicting Include and Exclude constraints."); + } + } + private bool CheckRepeats(int[] array) + { + _dummyHashSet.Clear(); + foreach (var item in array) + { + if (_dummyHashSet.Contains(item)) return true; + _dummyHashSet.Add(item); + } + return false; + } +#endif + private static string CreateLogString(int worldID, int[] inc, int[] exc) + { +#if (DEBUG && !DISABLE_DEBUG) + string converter(int o) => EcsDebugUtility.GetGenericTypeName(EcsWorld.GetWorld(worldID).AllPools[o].ComponentType, 1); + return $"Inc({string.Join(", ", inc.Select(converter))}) Exc({string.Join(", ", exc.Select(converter))})"; +#else + return $"Inc({string.Join(", ", inc)}) Exc({string.Join(", ", exc)})"; // Release optimization +#endif + } + internal class DebuggerProxy + { + public readonly EcsWorld world; + public readonly int worldID; + public readonly EcsMaskChunck[] includedChunkMasks; + public readonly EcsMaskChunck[] excludedChunkMasks; + public readonly int[] included; + public readonly int[] excluded; + public readonly Type[] includedTypes; + public readonly Type[] excludedTypes; + + public DebuggerProxy(EcsMask mask) + { + world = EcsWorld.GetWorld(mask.worldID); + worldID = mask.worldID; + includedChunkMasks = mask.incChunckMasks; + excludedChunkMasks = mask.excChunckMasks; + included = mask.inc; + excluded = mask.exc; + Type converter(int o) => world.GetComponentType(o); + includedTypes = included.Select(converter).ToArray(); + excludedTypes = excluded.Select(converter).ToArray(); + } + public override string ToString() => CreateLogString(worldID, included, excluded); + } + #endregion + + public readonly struct BuilderMaskKey : IEquatable + { + public readonly int[] inc; + public readonly int[] exc; + public readonly int hash; + public BuilderMaskKey(int[] inc, int[] exc, int hash) + { + this.inc = inc; + this.exc = exc; + this.hash = hash; + } + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public bool Equals(BuilderMaskKey other) + { + if (inc.Length != other.inc.Length) + { + return false; + } + if (exc.Length != other.exc.Length) + { + return false; + } + for (int i = 0; i < inc.Length; i++) + { + if (inc[i] != other.inc[i]) + { + return false; + } + } + for (int i = 0; i < exc.Length; i++) + { + if (exc[i] != other.exc[i]) + { + return false; + } + } +#if DEBUG + if(other.hash != hash) + { + throw new Exception("other.hash != hash"); + } +#endif + return true; + } + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public override int GetHashCode() => hash; + } + + public class Builder + { + private EcsWorld _world; + private HashSet _inc; + private HashSet _exc; + private List _combined; + + internal Builder(EcsWorld world) + { + _world = world; + _inc = new HashSet(); + _exc = new HashSet(); + } + + public void Include() + { + int id = _world.GetComponentID(); +#if (DEBUG && !DISABLE_DEBUG) || ENABLE_DRAGONECS_ASSERT_CHEKS + if (_inc.Contains(id) || _exc.Contains(id)) Throw.ConstraintIsAlreadyContainedInMask(typeof(T)); +#endif + _inc.Add(id); + } + public void Exclude() + { + int id = _world.GetComponentID(); +#if (DEBUG && !DISABLE_DEBUG) || ENABLE_DRAGONECS_ASSERT_CHEKS + if (_inc.Contains(id) || _exc.Contains(id)) Throw.ConstraintIsAlreadyContainedInMask(typeof(T)); +#endif + _exc.Add(id); + } + public void Include(Type type) + { + int id = _world.GetComponentID(type); +#if (DEBUG && !DISABLE_DEBUG) || ENABLE_DRAGONECS_ASSERT_CHEKS + if (_inc.Contains(id) || _exc.Contains(id)) Throw.ConstraintIsAlreadyContainedInMask(type); +#endif + _inc.Add(id); + } + public void Exclude(Type type) + { + int id = _world.GetComponentID(type); +#if (DEBUG && !DISABLE_DEBUG) || ENABLE_DRAGONECS_ASSERT_CHEKS + if (_inc.Contains(id) || _exc.Contains(id)) Throw.ConstraintIsAlreadyContainedInMask(type); +#endif + _exc.Add(id); + } + + public void CombineWith(EcsMask mask, int order = 0) + { + _combined.Add(new Combined(mask, order)); + } + + public EcsMask Build() + { + HashSet combinedInc; + HashSet combinedExc; + if (_combined.Count > 0) + { + combinedInc = new HashSet(); + combinedExc = new HashSet(); + _combined.Sort((a, b) => a.order - b.order); + foreach (var item in _combined) + { + EcsMask submask = item.mask; + combinedInc.ExceptWith(submask.exc);//удаляю конфликтующие ограничения + combinedExc.ExceptWith(submask.inc);//удаляю конфликтующие ограничения + combinedInc.UnionWith(submask.inc); + combinedExc.UnionWith(submask.exc); + } + combinedInc.ExceptWith(_exc);//удаляю конфликтующие ограничения + combinedExc.ExceptWith(_inc);//удаляю конфликтующие ограничения + combinedInc.UnionWith(_inc); + combinedExc.UnionWith(_exc); + } + else + { + combinedInc = _inc; + combinedExc = _exc; + } + + var inc = combinedInc.ToArray(); + Array.Sort(inc); + var exc = combinedExc.ToArray(); + Array.Sort(exc); + + int keyHash = inc.Length + exc.Length; + for (int i = 0, iMax = inc.Length; i < iMax; i++) + { + keyHash = unchecked(keyHash * 314159 + inc[i]); + } + for (int i = 0, iMax = exc.Length; i < iMax; i++) + { + keyHash = unchecked(keyHash * 314159 - exc[i]); + } + BuilderMaskKey key = new BuilderMaskKey(inc, exc, keyHash); + + _world = null; + _inc = null; + _exc = null; + _combined = null; + + return _world.GetMask_Internal(key); + } + } + + private readonly struct Combined + { + public readonly EcsMask mask; + public readonly int order; + public Combined(EcsMask mask, int order) + { + this.mask = mask; + this.order = order; + } + } + } + + [DebuggerTypeProxy(typeof(DebuggerProxy))] + public readonly struct EcsMaskChunck + { + internal const int BITS = 32; + internal const int DIV_SHIFT = 5; + internal const int MOD_MASK = BITS - 1; + + public readonly int chankIndex; + public readonly int mask; + public EcsMaskChunck(int chankIndex, int mask) + { + this.chankIndex = chankIndex; + this.mask = mask; + } + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static EcsMaskChunck FromID(int id) + { + return new EcsMaskChunck(id >> DIV_SHIFT, 1 << (id & MOD_MASK)); + } + public override string ToString() + { + return $"mask({chankIndex}, {mask}, {BitsUtility.CountBits(mask)})"; + } + internal class DebuggerProxy + { + public int chunk; + public uint mask; + public int[] values = Array.Empty(); + public string bits; + public DebuggerProxy(EcsMaskChunck maskbits) + { + chunk = maskbits.chankIndex; + mask = (uint)maskbits.mask; + BitsUtility.GetBitNumbersNoAlloc(mask, ref values); + for (int i = 0; i < values.Length; i++) + { + values[i] += (chunk) << 5; + } + bits = BitsUtility.ToBitsString(mask, '_', 8); + } + } + } +} diff --git a/src/EcsWorld.pools.cs b/src/EcsWorld.pools.cs index 5c61b60..06af5e4 100644 --- a/src/EcsWorld.pools.cs +++ b/src/EcsWorld.pools.cs @@ -125,14 +125,14 @@ namespace DCFApixels.DragonECS #region Pools mediation [MethodImpl(MethodImplOptions.AggressiveInlining)] - private void RegisterEntityComponent(int entityID, int componentTypeID, EcsMaskBit maskBit) + private void RegisterEntityComponent(int entityID, int componentTypeID, EcsMaskChunck maskBit) { _poolComponentCounts[componentTypeID]++; _componentCounts[entityID]++; _entitiesComponentMasks[entityID][maskBit.chankIndex] |= maskBit.mask; } [MethodImpl(MethodImplOptions.AggressiveInlining)] - private void UnregisterEntityComponent(int entityID, int componentTypeID, EcsMaskBit maskBit) + private void UnregisterEntityComponent(int entityID, int componentTypeID, EcsMaskChunck maskBit) { _poolComponentCounts[componentTypeID]--; var count = --_componentCounts[entityID]; @@ -145,7 +145,7 @@ namespace DCFApixels.DragonECS #endif } [MethodImpl(MethodImplOptions.AggressiveInlining)] - private bool HasEntityComponent(int entityID, EcsMaskBit maskBit) + private bool HasEntityComponent(int entityID, EcsMaskChunck maskBit) { return (_entitiesComponentMasks[entityID][maskBit.chankIndex] & maskBit.mask) != maskBit.mask; } @@ -164,17 +164,17 @@ namespace DCFApixels.DragonECS _world = world; } [MethodImpl(MethodImplOptions.AggressiveInlining)] - public void RegisterComponent(int entityID, int componentTypeID, EcsMaskBit maskBit) + public void RegisterComponent(int entityID, int componentTypeID, EcsMaskChunck maskBit) { _world.RegisterEntityComponent(entityID, componentTypeID, maskBit); } [MethodImpl(MethodImplOptions.AggressiveInlining)] - public void UnregisterComponent(int entityID, int componentTypeID, EcsMaskBit maskBit) + public void UnregisterComponent(int entityID, int componentTypeID, EcsMaskChunck maskBit) { _world.UnregisterEntityComponent(entityID, componentTypeID, maskBit); } [MethodImpl(MethodImplOptions.AggressiveInlining)] - public bool HasComponent(int entityID, EcsMaskBit maskBit) + public bool HasComponent(int entityID, EcsMaskChunck maskBit) { return _world.HasEntityComponent(entityID, maskBit); } diff --git a/src/Pools/EcsHybridPool.cs b/src/Pools/EcsHybridPool.cs index e48ecda..7c40337 100644 --- a/src/Pools/EcsHybridPool.cs +++ b/src/Pools/EcsHybridPool.cs @@ -21,7 +21,7 @@ namespace DCFApixels.DragonECS { private EcsWorld _source; private int _componentTypeID; - private EcsMaskBit _maskBit; + private EcsMaskChunck _maskBit; private int[] _mapping;// index = entityID / value = itemIndex;/ value = 0 = no entityID private T[] _items; //dense @@ -175,7 +175,7 @@ namespace DCFApixels.DragonECS _source = world; _mediator = mediator; _componentTypeID = componentTypeID; - _maskBit = EcsMaskBit.FromID(componentTypeID); + _maskBit = EcsMaskChunck.FromID(componentTypeID); const int capacity = 512; diff --git a/src/Pools/EcsPool.cs b/src/Pools/EcsPool.cs index 315422b..2f0755a 100644 --- a/src/Pools/EcsPool.cs +++ b/src/Pools/EcsPool.cs @@ -11,7 +11,7 @@ namespace DCFApixels.DragonECS { private EcsWorld _source; private int _componentTypeID; - private EcsMaskBit _maskBit; + private EcsMaskChunck _maskBit; private int[] _mapping;// index = entityID / value = itemIndex;/ value = 0 = no entityID private T[] _items; //dense @@ -141,7 +141,7 @@ namespace DCFApixels.DragonECS _source = world; _mediator = mediator; _componentTypeID = componentTypeID; - _maskBit = EcsMaskBit.FromID(componentTypeID); + _maskBit = EcsMaskChunck.FromID(componentTypeID); const int capacity = 512; diff --git a/src/Pools/EcsTagPool.cs b/src/Pools/EcsTagPool.cs index 86529de..834aa14 100644 --- a/src/Pools/EcsTagPool.cs +++ b/src/Pools/EcsTagPool.cs @@ -11,7 +11,7 @@ namespace DCFApixels.DragonECS { private EcsWorld _source; private int _componentTypeID; - private EcsMaskBit _maskBit; + private EcsMaskChunck _maskBit; private bool[] _mapping;// index = entityID / value = itemIndex;/ value = 0 = no entityID private int _count; @@ -124,7 +124,7 @@ namespace DCFApixels.DragonECS _source = world; _mediator = mediator; _componentTypeID = componentTypeID; - _maskBit = EcsMaskBit.FromID(componentTypeID); + _maskBit = EcsMaskChunck.FromID(componentTypeID); _mapping = new bool[world.Capacity]; _count = 0; diff --git a/src/Utils/ArraySortHalperX.cs b/src/Utils/ArraySortHalperX.cs new file mode 100644 index 0000000..3bc60be --- /dev/null +++ b/src/Utils/ArraySortHalperX.cs @@ -0,0 +1,179 @@ +using System; +using System.Runtime.CompilerServices; + + +namespace DCFApixels.DragonECS.Internal +{ + internal interface IComparerX + { + // a > b = return > 0 + int Compare(T a, T b); + } + internal static class ArraySortHalperX + { + private const int IntrosortSizeThreshold = 16; + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static void SwapIfGreater(T[] items, ref TComparer comparer, int i, int j) where TComparer : IComparerX + { + if (comparer.Compare(items[i], items[j]) > 0) + { + T key = items[i]; + items[i] = items[j]; + items[j] = key; + } + } + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static void InsertionSort(T[] items, ref TComparer comparer) where TComparer : IComparerX + { + for (int i = 0; i < items.Length - 1; i++) + { + T t = items[i + 1]; + + int j = i; + while (j >= 0 && comparer.Compare(t, items[j]) < 0) + { + items[j + 1] = items[j]; + j--; + } + + items[j + 1] = t; + } + } + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static void Sort(T[] items, ref TComparer comparer) where TComparer : IComparerX + { + int length = items.Length; + if (length == 1) + { + return; + } + + if (length <= IntrosortSizeThreshold) + { + + if (length == 2) + { + SwapIfGreater(items, ref comparer, 0, 1); + return; + } + + if (length == 3) + { + SwapIfGreater(items, ref comparer, 0, 1); + SwapIfGreater(items, ref comparer, 0, 2); + SwapIfGreater(items, ref comparer, 1, 2); + return; + } + + InsertionSort(items, ref comparer); + return; + } + IComparerX packed = comparer; + Array.Sort(items, comparer.Compare); + comparer = (TComparer)packed; + } + } + + internal static unsafe class UnsafeArraySortHalperX where T : unmanaged + { + private const int IntrosortSizeThreshold = 16; + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static void SwapIfGreater(T* items, ref TComparer comparer, int i, int j) where TComparer : IComparerX + { + if (comparer.Compare(items[i], items[j]) > 0) + { + T key = items[i]; + items[i] = items[j]; + items[j] = key; + } + } + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static void InsertionSort_Unchecked(T* items, int length, ref TComparer comparer) where TComparer : IComparerX + { + for (int i = 0; i < length - 1; i++) + { + T t = items[i + 1]; + + int j = i; + while (j >= 0 && comparer.Compare(t, items[j]) < 0) + { + items[j + 1] = items[j]; + j--; + } + + items[j + 1] = t; + } + } + //[MethodImpl(MethodImplOptions.AggressiveInlining)] + //public static void OptimizedBubbleSort_Unchecked(T* items, int length, ref TComparer comparer) where TComparer : IComparerX + //{ + // for (int i = 0, n = length - 1; i < n; i++) + // { + // bool noSwaped = true; + // for (int j = 0; j < n - i;) + // { + // ref T j0 = ref items[j++]; + // if(comparer.Compare(j0, items[j]) < 0) + // { + // T tmp = items[j]; + // items[j] = j0; + // j0 = tmp; + // noSwaped = false; + // } + // } + // if (noSwaped) + // break; + // } + //} + public static void InsertionSort(T* items, int length, ref TComparer comparer) where TComparer : IComparerX + { + if (length == 1) + { + return; + } + + if (length == 2) + { + SwapIfGreater(items, ref comparer, 0, 1); + return; + } + + if (length == 3) + { + SwapIfGreater(items, ref comparer, 0, 1); + SwapIfGreater(items, ref comparer, 0, 2); + SwapIfGreater(items, ref comparer, 1, 2); + return; + } + + InsertionSort_Unchecked(items, length, ref comparer); + } + //public static void OptimizedBubbleSort(T* items, int length, ref TComparer comparer) where TComparer : IComparerX + //{ + // if (length == 1) + // { + // return; + // } + // + // if (length == 2) + // { + // SwapIfGreater(items, ref comparer, 0, 1); + // return; + // } + // + // if (length == 3) + // { + // SwapIfGreater(items, ref comparer, 0, 1); + // SwapIfGreater(items, ref comparer, 0, 2); + // SwapIfGreater(items, ref comparer, 1, 2); + // return; + // } + // + // OptimizedBubbleSort_Unchecked(items, length, ref comparer); + //} + } +} + + diff --git a/src/Utils/ArrayUtility.cs b/src/Utils/ArrayUtility.cs index 5ff3c32..1a80faa 100644 --- a/src/Utils/ArrayUtility.cs +++ b/src/Utils/ArrayUtility.cs @@ -1,4 +1,5 @@ using System; +using System.Collections; using System.Collections.Generic; using System.Linq; using System.Runtime.CompilerServices; @@ -18,7 +19,46 @@ namespace DCFApixels.DragonECS.Utils array[i] = value; } } + internal readonly struct EnumerableInt : IEnumerable + { + public readonly int start; + public readonly int length; + private EnumerableInt(int start, int length) + { + this.start = start; + this.length = length; + } + public static EnumerableInt Range(int start, int length) => new EnumerableInt(start, length); + public static EnumerableInt StartEnd(int start, int end) => new EnumerableInt(start, end - start); + IEnumerator IEnumerable.GetEnumerator() => GetEnumerator(); + IEnumerator IEnumerable.GetEnumerator() => GetEnumerator(); + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public Enumerator GetEnumerator() => new Enumerator(start, start + length); + public struct Enumerator : IEnumerator + { + private readonly int _max; + private int _current; + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public Enumerator(int max, int current) + { + _max = max; + _current = current - 1; + } + public int Current + { + [MethodImpl(MethodImplOptions.AggressiveInlining)] + get => _current; + } + object IEnumerator.Current => Current; + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public bool MoveNext() => ++_current < _max; + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public void Reset() { } + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public void Dispose() { } + } + } internal static unsafe class UnmanagedArrayUtility { [MethodImpl(MethodImplOptions.AggressiveInlining)] @@ -26,6 +66,11 @@ namespace DCFApixels.DragonECS.Utils { return (T*)Marshal.AllocHGlobal(Marshal.SizeOf() * capacity).ToPointer(); } + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static void New(out T* ptr, int capacity) where T : unmanaged + { + ptr = (T*)Marshal.AllocHGlobal(Marshal.SizeOf() * capacity).ToPointer(); + } [MethodImpl(MethodImplOptions.AggressiveInlining)] public static T* NewAndInit(int capacity) where T : unmanaged @@ -38,12 +83,39 @@ namespace DCFApixels.DragonECS.Utils return (T*)newPointer; } + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static void NewAndInit(out T* ptr, int capacity) where T : unmanaged + { + int newSize = Marshal.SizeOf(typeof(T)) * capacity; + byte* newPointer = (byte*)Marshal.AllocHGlobal(newSize).ToPointer(); + + for (int i = 0; i < newSize; i++) + *(newPointer + i) = 0; + + ptr = (T*)newPointer; + } [MethodImpl(MethodImplOptions.AggressiveInlining)] public static void Free(void* pointer) { Marshal.FreeHGlobal(new IntPtr(pointer)); } + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static void Free(ref T* pointer, ref int length) where T : unmanaged + { + Marshal.FreeHGlobal(new IntPtr(pointer)); + pointer = null; + length = 0; + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static T* Clone(T* sourcePtr, int length) where T : unmanaged + { + T* clone = New(length); + for (int i = 0; i < length; i++) + clone[i] = sourcePtr[i]; + return clone; + } [MethodImpl(MethodImplOptions.AggressiveInlining)] public static T* Resize(void* oldPointer, int newCount) where T : unmanaged @@ -54,11 +126,15 @@ namespace DCFApixels.DragonECS.Utils } } - public static class EntitiesCollectionUtility + public static class CollectionUtility { - public static string AutoToString(IEnumerable range, string name) + public static string EntitiesToString(IEnumerable range, string name) { return $"{name}({range.Count()}) {{{string.Join(", ", range.OrderBy(o => o))}}})"; } + public static string AutoToString(IEnumerable range, string name) + { + return $"{name}({range.Count()}) {{{string.Join(", ", range.Select(o => o.ToString()))}}})"; + } } -} +} \ No newline at end of file From e9cdfbfb5104b1c2270ef8acc7d1665a620713ec Mon Sep 17 00:00:00 2001 From: Mikhail <99481254+DCFApixels@users.noreply.github.com> Date: Sun, 7 Jan 2024 19:32:16 +0800 Subject: [PATCH 05/11] update mask building --- src/Collections/EcsGroup.cs | 2 +- src/Collections/EcsSpan.cs | 2 +- src/Consts.cs | 2 + src/EcsAspect.cs | 98 ++++-------------------------- src/EcsMask.cs | 117 +++++++++++++++++++----------------- src/EcsWorld.cache.cs | 2 +- src/EcsWorld.cs | 2 +- src/EcsWorld.static.cs | 2 +- 8 files changed, 82 insertions(+), 145 deletions(-) diff --git a/src/Collections/EcsGroup.cs b/src/Collections/EcsGroup.cs index 7d8d441..2909f45 100644 --- a/src/Collections/EcsGroup.cs +++ b/src/Collections/EcsGroup.cs @@ -514,7 +514,7 @@ namespace DCFApixels.DragonECS #if (DEBUG && !DISABLE_DEBUG) || ENABLE_DRAGONECS_ASSERT_CHEKS if (_source != group.World) Throw.Group_ArgumentDifferentWorldsException(); #endif - if(group.Count > Count) + if (group.Count > Count) { foreach (var item in this) if (group.Has(item)) diff --git a/src/Collections/EcsSpan.cs b/src/Collections/EcsSpan.cs index be3e2e7..579d916 100644 --- a/src/Collections/EcsSpan.cs +++ b/src/Collections/EcsSpan.cs @@ -37,7 +37,7 @@ namespace DCFApixels.DragonECS { [MethodImpl(MethodImplOptions.AggressiveInlining)] get => _values.IsEmpty; - } + } #endregion #region Constructors diff --git a/src/Consts.cs b/src/Consts.cs index 8100e90..5ab4b62 100644 --- a/src/Consts.cs +++ b/src/Consts.cs @@ -17,5 +17,7 @@ public const string POST_END_LAYER = nameof(POST_END_LAYER); public const string META_HIDDEN_TAG = "HiddenInDebagging"; + + public const int MAGIC_PRIME = 314159; } } diff --git a/src/EcsAspect.cs b/src/EcsAspect.cs index 924a081..cbcbb0c 100644 --- a/src/EcsAspect.cs +++ b/src/EcsAspect.cs @@ -2,7 +2,6 @@ using DCFApixels.DragonECS.Utils; using System; using System.Collections.Generic; -using System.Linq; using System.Reflection; using System.Runtime.CompilerServices; @@ -34,20 +33,16 @@ namespace DCFApixels.DragonECS public sealed class Builder : EcsAspectBuilderBase { private EcsWorld _world; - private HashSet _inc; - private HashSet _exc; - private List _combined; + private EcsMask.Builder _maskBuilder; public EcsWorld World => _world; private Builder(EcsWorld world) { _world = world; - _combined = new List(); - _inc = new HashSet(); - _exc = new HashSet(); + _maskBuilder = new EcsMask.Builder(world); } - internal static unsafe TAspect Build(EcsWorld world) where TAspect : EcsAspect + internal static unsafe TAspect New(EcsWorld world) where TAspect : EcsAspect { Builder builder = new Builder(world); Type aspectType = typeof(TAspect); @@ -63,7 +58,7 @@ namespace DCFApixels.DragonECS newAspect.Init(builder); } newAspect._source = world; - builder.End(out newAspect._mask); + builder.Build(out newAspect._mask); newAspect._isInit = true; newAspect._sortIncBuffer = new UnsafeArray(newAspect._mask.inc.Length, true); @@ -92,7 +87,7 @@ namespace DCFApixels.DragonECS return (TAspect)newAspect; } - #region Include/Exclude/Optional + #region Include/Exclude/Optional/Combine public sealed override TPool Include() { IncludeImplicit(typeof(TPool).GetGenericArguments()[0]); @@ -109,27 +104,16 @@ namespace DCFApixels.DragonECS } private void IncludeImplicit(Type type) { - int id = _world.GetComponentID(type); -#if (DEBUG && !DISABLE_DEBUG) || ENABLE_DRAGONECS_ASSERT_CHEKS - if (_inc.Contains(id) || _exc.Contains(id)) Throw.ConstraintIsAlreadyContainedInMask(type); -#endif - _inc.Add(id); + _maskBuilder.Include(type); } private void ExcludeImplicit(Type type) { - int id = _world.GetComponentID(type); -#if (DEBUG && !DISABLE_DEBUG) || ENABLE_DRAGONECS_ASSERT_CHEKS - if (_inc.Contains(id) || _exc.Contains(id)) Throw.ConstraintIsAlreadyContainedInMask(type); -#endif - _exc.Add(id); + _maskBuilder.Exclude(type); } - #endregion - - #region Combine public TOtherAspect Combine(int order = 0) where TOtherAspect : EcsAspect { var result = _world.GetAspect(); - _combined.Add(new Combined(result, order)); + _maskBuilder.CombineWith(result.Mask); return result; } #endregion @@ -139,60 +123,9 @@ namespace DCFApixels.DragonECS return new EcsWorldCmp(_world.id); } - private void End(out EcsMask mask) + private void Build(out EcsMask mask) { - HashSet maskInc; - HashSet maskExc; - if (_combined.Count > 0) - { - maskInc = new HashSet(); - maskExc = new HashSet(); - _combined.Sort((a, b) => a.order - b.order); - foreach (var item in _combined) - { - EcsMask submask = item.aspect._mask; - maskInc.ExceptWith(submask.exc);//удаляю конфликтующие ограничения - maskExc.ExceptWith(submask.inc);//удаляю конфликтующие ограничения - maskInc.UnionWith(submask.inc); - maskExc.UnionWith(submask.exc); - } - maskInc.ExceptWith(_exc);//удаляю конфликтующие ограничения - maskExc.ExceptWith(_inc);//удаляю конфликтующие ограничения - maskInc.UnionWith(_inc); - maskExc.UnionWith(_exc); - } - else - { - maskInc = _inc; - maskExc = _exc; - } - - Dictionary r = new Dictionary(); - foreach (var id in maskInc) - { - var bit = EcsMaskChunck.FromID(id); - if (!r.TryAdd(bit.chankIndex, bit.mask)) - r[bit.chankIndex] = r[bit.chankIndex] | bit.mask; - } - EcsMaskChunck[] incMasks = r.Select(o => new EcsMaskChunck(o.Key, o.Value)).ToArray(); - r.Clear(); - foreach (var id in maskExc) - { - var bit = EcsMaskChunck.FromID(id); - if (!r.TryAdd(bit.chankIndex, bit.mask)) - r[bit.chankIndex] = r[bit.chankIndex] | bit.mask; - } - EcsMaskChunck[] excMasks = r.Select(o => new EcsMaskChunck(o.Key, o.Value)).ToArray(); - - var inc = maskInc.ToArray(); - Array.Sort(inc); - var exc = maskExc.ToArray(); - Array.Sort(exc); - - mask = new EcsMask(0, _world.id, inc, exc, incMasks, excMasks); - _world = null; - _inc = null; - _exc = null; + mask = _maskBuilder.Build(); } #region SupportReflectionHack @@ -281,7 +214,7 @@ namespace DCFApixels.DragonECS int count = 0; while (enumerator.MoveNext()) { - if(array.Length <= count) + if (array.Length <= count) Array.Resize(ref array, array.Length << 1); array[count++] = enumerator.Current; } @@ -365,15 +298,11 @@ namespace DCFApixels.DragonECS UnsafeArray _sortExcBuffer = aspect._sortExcBuffer; _sortIncChunckBuffer = aspect._sortIncChunckBuffer; _sortExcChunckBuffer = aspect._sortExcChunckBuffer; - int[] counts = mask.World._poolComponentCounts; - - + #region Sort IncCountComparer incComparer = new IncCountComparer(counts); ExcCountComparer excComparer = new ExcCountComparer(counts); - - #region Sort UnsafeArraySortHalperX.InsertionSort(_sortIncBuffer.ptr, _sortIncBuffer.Length, ref incComparer); UnsafeArraySortHalperX.InsertionSort(_sortExcBuffer.ptr, _sortExcBuffer.Length, ref excComparer); @@ -476,9 +405,6 @@ namespace DCFApixels.DragonECS _preSortedExcBuffer[i] = EcsMaskChunck.FromID(_sortExcBuffer.ptr[i]); } - //int _sortedIncBufferLength = mask.inc.Length; - //int _sortedExcBufferLength = mask.exc.Length; - //if (_sortIncChunckBuffer.Length > 1)//перенести этот чек в начала сортировки, для _incChunckMasks.Length == 1 сортировка не нужна if (_sortIncBuffer.Length > 1) { diff --git a/src/EcsMask.cs b/src/EcsMask.cs index f3da598..10b2b71 100644 --- a/src/EcsMask.cs +++ b/src/EcsMask.cs @@ -4,7 +4,6 @@ using System.Collections.Generic; using System.Diagnostics; using System.Linq; using System.Runtime.CompilerServices; -using static Leopotam.EcsLite.EcsWorld; namespace DCFApixels.DragonECS { @@ -13,38 +12,17 @@ namespace DCFApixels.DragonECS private Dictionary _masks = new Dictionary(256); internal EcsMask GetMask_Internal(EcsMask.BuilderMaskKey maskKey) { - if(!_masks.TryGetValue(maskKey, out EcsMask result)) + if (!_masks.TryGetValue(maskKey, out EcsMask result)) { - int[] combinedInc = maskKey.inc; - int[] combinedExc = maskKey.exc; - Dictionary r = new Dictionary(); - foreach (var id in combinedInc) - { - var bit = EcsMaskChunck.FromID(id); - if (!r.TryAdd(bit.chankIndex, bit.mask)) - r[bit.chankIndex] = r[bit.chankIndex] | bit.mask; - } - EcsMaskChunck[] incMasks = r.Select(o => new EcsMaskChunck(o.Key, o.Value)).ToArray(); - r.Clear(); - foreach (var id in combinedExc) - { - var bit = EcsMaskChunck.FromID(id); - if (!r.TryAdd(bit.chankIndex, bit.mask)) - r[bit.chankIndex] = r[bit.chankIndex] | bit.mask; - } - EcsMaskChunck[] excMasks = r.Select(o => new EcsMaskChunck(o.Key, o.Value)).ToArray(); - - result = new EcsMask(_masks.Count, id, combinedInc, combinedExc, incMasks, excMasks); + result = new EcsMask(_masks.Count, id, maskKey.inc, maskKey.exc); _masks.Add(maskKey, result); } return result; } } - - [DebuggerTypeProxy(typeof(DebuggerProxy))] - public sealed class EcsMask + public sealed class EcsMask : IEquatable { internal readonly int id; internal readonly int worldID; @@ -58,7 +36,7 @@ namespace DCFApixels.DragonECS public ReadOnlySpan Inc => inc; /// Excluding constraints public ReadOnlySpan Exc => exc; - internal EcsMask(int id, int worldID, int[] inc, int[] exc, EcsMaskChunck[] incChunckMasks, EcsMaskChunck[] excChunckMasks) + internal EcsMask(int id, int worldID, int[] inc, int[] exc) { #if DEBUG CheckConstraints(inc, exc); @@ -67,12 +45,43 @@ namespace DCFApixels.DragonECS this.inc = inc; this.exc = exc; this.worldID = worldID; - this.incChunckMasks = incChunckMasks; - this.excChunckMasks = excChunckMasks; + + //TODO пересесть с дикта на подсчет в цикле. + Dictionary r = new Dictionary(); + foreach (var cmpID in inc) + { + var bit = EcsMaskChunck.FromID(cmpID); + if (!r.TryAdd(bit.chankIndex, bit.mask)) + r[bit.chankIndex] = r[bit.chankIndex] | bit.mask; + } + EcsMaskChunck[] incMasks = r.Select(o => new EcsMaskChunck(o.Key, o.Value)).ToArray(); + r.Clear(); + foreach (var cmpID in exc) + { + var bit = EcsMaskChunck.FromID(cmpID); + if (!r.TryAdd(bit.chankIndex, bit.mask)) + r[bit.chankIndex] = r[bit.chankIndex] | bit.mask; + } + EcsMaskChunck[] excMasks = r.Select(o => new EcsMaskChunck(o.Key, o.Value)).ToArray(); + + incChunckMasks = incMasks; + excChunckMasks = excMasks; } #region Object public override string ToString() => CreateLogString(worldID, inc, exc); + public bool Equals(EcsMask mask) + { + return id == mask.id && worldID == mask.worldID; + } + public override bool Equals(object obj) + { + return obj is EcsMask mask && id == mask.id && Equals(mask); + } + public override int GetHashCode() + { + return unchecked(id ^ (worldID * EcsConsts.MAGIC_PRIME)); + } #endregion #region Debug utils @@ -109,10 +118,12 @@ namespace DCFApixels.DragonECS return $"Inc({string.Join(", ", inc)}) Exc({string.Join(", ", exc)})"; // Release optimization #endif } + internal class DebuggerProxy { + public readonly int ID; public readonly EcsWorld world; - public readonly int worldID; + private readonly int _worldID; public readonly EcsMaskChunck[] includedChunkMasks; public readonly EcsMaskChunck[] excludedChunkMasks; public readonly int[] included; @@ -122,8 +133,9 @@ namespace DCFApixels.DragonECS public DebuggerProxy(EcsMask mask) { + ID = mask.id; world = EcsWorld.GetWorld(mask.worldID); - worldID = mask.worldID; + _worldID = mask.worldID; includedChunkMasks = mask.incChunckMasks; excludedChunkMasks = mask.excChunckMasks; included = mask.inc; @@ -132,10 +144,11 @@ namespace DCFApixels.DragonECS includedTypes = included.Select(converter).ToArray(); excludedTypes = excluded.Select(converter).ToArray(); } - public override string ToString() => CreateLogString(worldID, included, excluded); + public override string ToString() => CreateLogString(_worldID, included, excluded); } #endregion + #region Builder public readonly struct BuilderMaskKey : IEquatable { public readonly int[] inc; @@ -173,7 +186,7 @@ namespace DCFApixels.DragonECS } } #if DEBUG - if(other.hash != hash) + if (other.hash != hash) { throw new Exception("other.hash != hash"); } @@ -186,16 +199,14 @@ namespace DCFApixels.DragonECS public class Builder { - private EcsWorld _world; - private HashSet _inc; - private HashSet _exc; - private List _combined; + private readonly EcsWorld _world; + private readonly HashSet _inc = new HashSet(); + private readonly HashSet _exc = new HashSet(); + private readonly List _combined = new List(); internal Builder(EcsWorld world) { _world = world; - _inc = new HashSet(); - _exc = new HashSet(); } public void Include() @@ -269,26 +280,23 @@ namespace DCFApixels.DragonECS var exc = combinedExc.ToArray(); Array.Sort(exc); - int keyHash = inc.Length + exc.Length; - for (int i = 0, iMax = inc.Length; i < iMax; i++) + unchecked { - keyHash = unchecked(keyHash * 314159 + inc[i]); + int keyHash = inc.Length + exc.Length; + for (int i = 0, iMax = inc.Length; i < iMax; i++) + { + keyHash = keyHash * EcsConsts.MAGIC_PRIME + inc[i]; + } + for (int i = 0, iMax = exc.Length; i < iMax; i++) + { + keyHash = keyHash * EcsConsts.MAGIC_PRIME - exc[i]; + } + BuilderMaskKey key = new BuilderMaskKey(inc, exc, keyHash); + return _world.GetMask_Internal(key); } - for (int i = 0, iMax = exc.Length; i < iMax; i++) - { - keyHash = unchecked(keyHash * 314159 - exc[i]); - } - BuilderMaskKey key = new BuilderMaskKey(inc, exc, keyHash); - - _world = null; - _inc = null; - _exc = null; - _combined = null; - - return _world.GetMask_Internal(key); } } - + private readonly struct Combined { public readonly EcsMask mask; @@ -299,6 +307,7 @@ namespace DCFApixels.DragonECS this.order = order; } } + #endregion } [DebuggerTypeProxy(typeof(DebuggerProxy))] diff --git a/src/EcsWorld.cache.cs b/src/EcsWorld.cache.cs index 8729c4b..a964ce5 100644 --- a/src/EcsWorld.cache.cs +++ b/src/EcsWorld.cache.cs @@ -23,7 +23,7 @@ public AspectCache(T instance) => this.instance = instance; void IEcsWorldComponent>.Init(ref AspectCache component, EcsWorld world) { - component = new AspectCache(EcsAspect.Builder.Build(world)); + component = new AspectCache(EcsAspect.Builder.New(world)); } void IEcsWorldComponent>.OnDestroy(ref AspectCache component, EcsWorld world) { diff --git a/src/EcsWorld.cs b/src/EcsWorld.cs index 6561464..137665c 100644 --- a/src/EcsWorld.cs +++ b/src/EcsWorld.cs @@ -142,7 +142,7 @@ namespace DCFApixels.DragonECS #region Where Query public EcsReadonlyGroup WhereToGroupFor(EcsSpan span, out TAspect aspect) where TAspect : EcsAspect { - if(_isEnableReleaseDelEntBuffer) + if (_isEnableReleaseDelEntBuffer) { ReleaseDelEntityBufferAll(); } diff --git a/src/EcsWorld.static.cs b/src/EcsWorld.static.cs index 10c18e9..dd52fea 100644 --- a/src/EcsWorld.static.cs +++ b/src/EcsWorld.static.cs @@ -96,7 +96,7 @@ namespace DCFApixels.DragonECS { itemIndex = ++_count; } - if(_items.Length <= itemIndex) + if (_items.Length <= itemIndex) { Array.Resize(ref _items, _items.Length << 1); } From 41391e70380e7ddaa945ea1f275d0df9a15226bb Mon Sep 17 00:00:00 2001 From: Mikhail <99481254+DCFApixels@users.noreply.github.com> Date: Sun, 7 Jan 2024 20:44:11 +0800 Subject: [PATCH 06/11] optimization mask building --- src/EcsMask.cs | 51 +++++++++++++++++++++++++++++++------------------- 1 file changed, 32 insertions(+), 19 deletions(-) diff --git a/src/EcsMask.cs b/src/EcsMask.cs index 10b2b71..1017283 100644 --- a/src/EcsMask.cs +++ b/src/EcsMask.cs @@ -4,6 +4,7 @@ using System.Collections.Generic; using System.Diagnostics; using System.Linq; using System.Runtime.CompilerServices; +using System.Runtime.InteropServices; namespace DCFApixels.DragonECS { @@ -46,26 +47,38 @@ namespace DCFApixels.DragonECS this.exc = exc; this.worldID = worldID; - //TODO пересесть с дикта на подсчет в цикле. - Dictionary r = new Dictionary(); - foreach (var cmpID in inc) - { - var bit = EcsMaskChunck.FromID(cmpID); - if (!r.TryAdd(bit.chankIndex, bit.mask)) - r[bit.chankIndex] = r[bit.chankIndex] | bit.mask; - } - EcsMaskChunck[] incMasks = r.Select(o => new EcsMaskChunck(o.Key, o.Value)).ToArray(); - r.Clear(); - foreach (var cmpID in exc) - { - var bit = EcsMaskChunck.FromID(cmpID); - if (!r.TryAdd(bit.chankIndex, bit.mask)) - r[bit.chankIndex] = r[bit.chankIndex] | bit.mask; - } - EcsMaskChunck[] excMasks = r.Select(o => new EcsMaskChunck(o.Key, o.Value)).ToArray(); + incChunckMasks = MakeMaskChuncsArray(inc); + excChunckMasks = MakeMaskChuncsArray(exc); + } - incChunckMasks = incMasks; - excChunckMasks = excMasks; + private unsafe EcsMaskChunck[] MakeMaskChuncsArray(int[] sortedArray) + { + EcsMaskChunck* buffer = stackalloc EcsMaskChunck[sortedArray.Length]; + + int resultLength = 0; + for (int i = 0; i < sortedArray.Length;) + { + int chankIndexX = sortedArray[i] >> EcsMaskChunck.DIV_SHIFT; + int maskX = 0; + do + { + EcsMaskChunck bitJ = EcsMaskChunck.FromID(sortedArray[i]); + if (bitJ.chankIndex != chankIndexX) + { + break; + } + maskX |= bitJ.mask; + i++; + } while (i < sortedArray.Length); + buffer[resultLength++] = new EcsMaskChunck(chankIndexX, maskX); + } + + EcsMaskChunck[] result = new EcsMaskChunck[resultLength]; + for (int i = 0; i < resultLength; i++) + { + result[i] = buffer[i]; + } + return result; } #region Object From 14f1cf2856f64a3ed10a29033caa1227140b4475 Mon Sep 17 00:00:00 2001 From: Mikhail <99481254+DCFApixels@users.noreply.github.com> Date: Sun, 7 Jan 2024 20:45:42 +0800 Subject: [PATCH 07/11] Update EcsMask.cs --- src/EcsMask.cs | 1 - 1 file changed, 1 deletion(-) diff --git a/src/EcsMask.cs b/src/EcsMask.cs index 1017283..6cf6423 100644 --- a/src/EcsMask.cs +++ b/src/EcsMask.cs @@ -4,7 +4,6 @@ using System.Collections.Generic; using System.Diagnostics; using System.Linq; using System.Runtime.CompilerServices; -using System.Runtime.InteropServices; namespace DCFApixels.DragonECS { From 40141dfbb9f3c103c2ccf07f262a7f75075cacc1 Mon Sep 17 00:00:00 2001 From: Mikhail <99481254+DCFApixels@users.noreply.github.com> Date: Sun, 7 Jan 2024 23:19:18 +0800 Subject: [PATCH 08/11] update --- src/DataInterfaces.cs | 4 +- src/EcsAspect.cs | 152 ++++++++------------------------------- src/EcsMask.cs | 9 +++ src/Utils/UnsafeArray.cs | 95 ++++++++++++++++++++++++ 4 files changed, 135 insertions(+), 125 deletions(-) create mode 100644 src/Utils/UnsafeArray.cs diff --git a/src/DataInterfaces.cs b/src/DataInterfaces.cs index f64125c..ef4ef76 100644 --- a/src/DataInterfaces.cs +++ b/src/DataInterfaces.cs @@ -37,7 +37,7 @@ namespace DCFApixels.DragonECS internal class WorldComponentHandler : IEcsWorldComponent where T : IEcsWorldComponent { - private T _fakeInstnace; + private T _fakeInstnace = default; [MethodImpl(MethodImplOptions.AggressiveInlining)] public void Init(ref T component, EcsWorld world) => _fakeInstnace.Init(ref component, world); public void OnDestroy(ref T component, EcsWorld world) => _fakeInstnace.OnDestroy(ref component, world); @@ -112,7 +112,7 @@ namespace DCFApixels.DragonECS internal sealed class ComponentCopyHandler : IEcsComponentCopy where T : IEcsComponentCopy { - private T _fakeInstnace; + private T _fakeInstnace = default; [MethodImpl(MethodImplOptions.AggressiveInlining)] public void Copy(ref T from, ref T to) => _fakeInstnace.Copy(ref from, ref to); } diff --git a/src/EcsAspect.cs b/src/EcsAspect.cs index cbcbb0c..17c6369 100644 --- a/src/EcsAspect.cs +++ b/src/EcsAspect.cs @@ -251,6 +251,7 @@ namespace DCFApixels.DragonECS public unsafe ref struct Enumerator { + #region CountComparers private readonly struct IncCountComparer : IComparerX { public readonly int[] counts; @@ -275,6 +276,8 @@ namespace DCFApixels.DragonECS return counts[b] - counts[a]; } } + #endregion + private ReadOnlySpan.Enumerator _span; private readonly int[][] _entitiesComponentMasks; @@ -284,110 +287,20 @@ namespace DCFApixels.DragonECS private UnsafeArray _sortIncChunckBuffer; private UnsafeArray _sortExcChunckBuffer; - private EcsAspect aspect; + //private EcsAspect aspect; public unsafe Enumerator(EcsSpan span, EcsAspect aspect) { - this.aspect = aspect; + //this.aspect = aspect; _span = span.GetEnumerator(); - _entitiesComponentMasks = span.World._entitiesComponentMasks; - - EcsMask mask = aspect.Mask; - - UnsafeArray _sortIncBuffer = aspect._sortIncBuffer; - UnsafeArray _sortExcBuffer = aspect._sortExcBuffer; + _entitiesComponentMasks = aspect.World._entitiesComponentMasks; _sortIncChunckBuffer = aspect._sortIncChunckBuffer; _sortExcChunckBuffer = aspect._sortExcChunckBuffer; - int[] counts = mask.World._poolComponentCounts; #region Sort - IncCountComparer incComparer = new IncCountComparer(counts); - ExcCountComparer excComparer = new ExcCountComparer(counts); - UnsafeArraySortHalperX.InsertionSort(_sortIncBuffer.ptr, _sortIncBuffer.Length, ref incComparer); - UnsafeArraySortHalperX.InsertionSort(_sortExcBuffer.ptr, _sortExcBuffer.Length, ref excComparer); - - //if (_sortIncBuffer.Length > 1) - //{ - // //if (_sortIncBufferLength == 2) - // //{ - // // if (counts[_sortIncBuffer[0]] > counts[_sortIncBuffer[1]]) - // // { - // // int tmp = _sortIncBuffer[0]; - // // _sortIncBuffer[0] = _sortIncBuffer[1]; - // // _sortIncBuffer[1] = tmp; - // // } - // // //... - // //} - // //else - // { - // for (int i = 0, n = _sortIncBuffer.Length - 1; i < n; i++) - // { - // //int counti = counts[_sortIncBuffer[i]]; - // //if (counti <= 0) - // //{ - // // _span = ReadOnlySpan.Empty.GetEnumerator(); - // // goto skip1; - // //} - // bool noSwaped = true; - // for (int j = 0; j < n - i; ) - // { - // ref int j0 = ref _sortIncBuffer.ptr[j++]; - // if (counts[j0] > counts[_sortIncBuffer.ptr[j]]) - // { - // int tmp = _sortIncBuffer.ptr[j]; - // _sortIncBuffer.ptr[j] = j0; - // j0 = tmp; - // noSwaped = false; - // } - // } - // if (noSwaped) - // break; - // } - // } - //} - //skip1:; - //if (_sortExcBuffer.Length > 1) - //{ - // //if (_sortExcBufferLength == 2) - // //{ - // // if (counts[_sortExcBuffer[0]] < counts[_sortExcBuffer[1]]) - // // { - // // int tmp = _sortExcBuffer[0]; - // // _sortExcBuffer[0] = _sortExcBuffer[1]; - // // _sortExcBuffer[1] = tmp; - // // } - // // //... - // //} - // //else - // { - // for (int i = 0, n = _sortExcBuffer.Length - 1; i < n; i++) - // { - // //int counti = counts[_sortExcBuffer[i]]; - // //if (counti <= 0) - // //{ - // // _excChunckMasks = ReadOnlySpan.Empty; - // // goto skip2; - // //} - // bool noSwaped = true; - // for (int j = 0; j < n - i;) - // { - // ref int j0 = ref _sortExcBuffer.ptr[j++]; - // if (counts[j0] < counts[_sortExcBuffer.ptr[j]]) - // { - // int tmp = _sortExcBuffer.ptr[j]; - // _sortExcBuffer.ptr[j] = j0; - // j0 = tmp; - // noSwaped = false; - // } - // } - // if (noSwaped) - // break; - // } - // } - //} - //skip2:; - - + UnsafeArray _sortIncBuffer = aspect._sortIncBuffer; + UnsafeArray _sortExcBuffer = aspect._sortExcBuffer; + int[] counts = aspect.World._poolComponentCounts; if (_preSortedIncBuffer == null) { @@ -395,19 +308,14 @@ namespace DCFApixels.DragonECS _preSortedExcBuffer = UnmanagedArrayUtility.New(256); } - - for (int i = 0; i < _sortIncBuffer.Length; i++) - { - _preSortedIncBuffer[i] = EcsMaskChunck.FromID(_sortIncBuffer.ptr[i]); - } - for (int i = 0; i < _sortExcBuffer.Length; i++) - { - _preSortedExcBuffer[i] = EcsMaskChunck.FromID(_sortExcBuffer.ptr[i]); - } - - //if (_sortIncChunckBuffer.Length > 1)//перенести этот чек в начала сортировки, для _incChunckMasks.Length == 1 сортировка не нужна - if (_sortIncBuffer.Length > 1) + if (_sortIncChunckBuffer.Length > 1) { + IncCountComparer incComparer = new IncCountComparer(counts); + UnsafeArraySortHalperX.InsertionSort(_sortIncBuffer.ptr, _sortIncBuffer.Length, ref incComparer); + for (int i = 0; i < _sortIncBuffer.Length; i++) + { + _preSortedIncBuffer[i] = EcsMaskChunck.FromID(_sortIncBuffer.ptr[i]); + } for (int i = 0, ii = 0; ii < _sortIncChunckBuffer.Length; ii++) { EcsMaskChunck bas = _preSortedIncBuffer[i]; @@ -428,14 +336,16 @@ namespace DCFApixels.DragonECS } } } - else - { - _sortIncChunckBuffer.ptr[0] = _preSortedIncBuffer[0]; - } - //if (_sortExcChunckBuffer.Length > 1)//перенести этот чек в начала сортировки, для _excChunckMasks.Length == 1 сортировка не нужна - if (_sortExcBuffer.Length > 1) + if (_sortExcChunckBuffer.Length > 1) { + ExcCountComparer excComparer = new ExcCountComparer(counts); + UnsafeArraySortHalperX.InsertionSort(_sortExcBuffer.ptr, _sortExcBuffer.Length, ref excComparer); + for (int i = 0; i < _sortExcBuffer.Length; i++) + { + _preSortedExcBuffer[i] = EcsMaskChunck.FromID(_sortExcBuffer.ptr[i]); + } + for (int i = 0, ii = 0; ii < _sortExcChunckBuffer.Length; ii++) { EcsMaskChunck bas = _preSortedExcBuffer[i]; @@ -456,10 +366,6 @@ namespace DCFApixels.DragonECS } } } - else - { - _sortExcChunckBuffer.ptr[0] = _preSortedExcBuffer[0]; - } #endregion } public int Current @@ -467,11 +373,11 @@ namespace DCFApixels.DragonECS [MethodImpl(MethodImplOptions.AggressiveInlining)] get => _span.Current; } - public entlong CurrentLong - { - [MethodImpl(MethodImplOptions.AggressiveInlining)] - get => aspect.World.GetEntityLong(_span.Current); - } + //public entlong CurrentLong + //{ + // [MethodImpl(MethodImplOptions.AggressiveInlining)] + // get => aspect.World.GetEntityLong(_span.Current); + //} [MethodImpl(MethodImplOptions.AggressiveInlining)] public bool MoveNext() { diff --git a/src/EcsMask.cs b/src/EcsMask.cs index 6cf6423..f6c27b2 100644 --- a/src/EcsMask.cs +++ b/src/EcsMask.cs @@ -30,12 +30,18 @@ namespace DCFApixels.DragonECS internal readonly EcsMaskChunck[] excChunckMasks; internal readonly int[] inc; internal readonly int[] exc; + + #region Properties + public int ID => id; public int WorldID => worldID; public EcsWorld World => EcsWorld.GetWorld(worldID); /// Including constraints public ReadOnlySpan Inc => inc; /// Excluding constraints public ReadOnlySpan Exc => exc; + #endregion + + #region Constructors internal EcsMask(int id, int worldID, int[] inc, int[] exc) { #if DEBUG @@ -79,6 +85,7 @@ namespace DCFApixels.DragonECS } return result; } + #endregion #region Object public override string ToString() => CreateLogString(worldID, inc, exc); @@ -322,6 +329,7 @@ namespace DCFApixels.DragonECS #endregion } + #region EcsMaskChunck [DebuggerTypeProxy(typeof(DebuggerProxy))] public readonly struct EcsMaskChunck { @@ -364,4 +372,5 @@ namespace DCFApixels.DragonECS } } } + #endregion } diff --git a/src/Utils/UnsafeArray.cs b/src/Utils/UnsafeArray.cs new file mode 100644 index 0000000..23f7fd1 --- /dev/null +++ b/src/Utils/UnsafeArray.cs @@ -0,0 +1,95 @@ +using DCFApixels.DragonECS.Utils; +using System; +using System.Collections; +using System.Collections.Generic; +using System.Diagnostics; +using System.Linq; +using System.Runtime.CompilerServices; + +namespace DCFApixels.DragonECS.Internal +{ + [DebuggerTypeProxy(typeof(UnsafeArray<>.DebuggerProxy))] + internal unsafe struct UnsafeArray : IDisposable, IEnumerable + where T : unmanaged + { + internal T* ptr; + internal int Length; + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public UnsafeArray(int length) + { + UnmanagedArrayUtility.New(out ptr, length); + Length = length; + } + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public UnsafeArray(int length, bool isInit) + { + UnmanagedArrayUtility.NewAndInit(out ptr, length); + Length = length; + } + [MethodImpl(MethodImplOptions.AggressiveInlining)] + private UnsafeArray(T* ptr, int length) + { + this.ptr = ptr; + Length = length; + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public UnsafeArray Clone() + { + return new UnsafeArray(UnmanagedArrayUtility.Clone(ptr, Length), Length); + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public void Dispose() + { + UnmanagedArrayUtility.Free(ref ptr, ref Length); + } + public override string ToString() + { + T* ptr = this.ptr; + return CollectionUtility.AutoToString(EnumerableInt.Range(0, Length).Select(i => ptr[i]), "ua"); + } + + IEnumerator IEnumerable.GetEnumerator() => GetEnumerator(); + IEnumerator IEnumerable.GetEnumerator() => GetEnumerator(); + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public Enumerator GetEnumerator() => new Enumerator(ptr, Length); + public struct Enumerator : IEnumerator + { + private readonly T* _ptr; + private readonly int _length; + private int _index; + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public Enumerator(T* ptr, int length) + { + _ptr = ptr; + _length = length; + _index = -1; + } + public T Current + { + [MethodImpl(MethodImplOptions.AggressiveInlining)] + get => _ptr[_index]; + } + object IEnumerator.Current => Current; + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public bool MoveNext() => ++_index < _length; + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public void Reset() { } + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public void Dispose() { } + } + + internal class DebuggerProxy + { + public T[] elements; + public int length; + public DebuggerProxy(UnsafeArray instance) + { + elements = EnumerableInt.Range(0, instance.Length).Select(i => instance.ptr[i]).ToArray(); + length = instance.Length; + } + } + } +} From 94fdcab168201036b42e83d589edbaab927e3638 Mon Sep 17 00:00:00 2001 From: Mikhail <99481254+DCFApixels@users.noreply.github.com> Date: Sun, 7 Jan 2024 23:21:13 +0800 Subject: [PATCH 09/11] add bitsutility --- src/Utils/BitsUtility.cs | 1079 ++++++++++++++++++++++++++++++++++++++ 1 file changed, 1079 insertions(+) create mode 100644 src/Utils/BitsUtility.cs diff --git a/src/Utils/BitsUtility.cs b/src/Utils/BitsUtility.cs new file mode 100644 index 0000000..ab898bd --- /dev/null +++ b/src/Utils/BitsUtility.cs @@ -0,0 +1,1079 @@ +using System; +using System.Collections; +using System.Collections.Generic; +using System.Runtime.CompilerServices; +using System.Text.RegularExpressions; +using static DCFApixels.BitsUtility; + +namespace DCFApixels +{ + public unsafe static class BitsUtility + { + private const char DEFAULT_SEPARATOR = '_'; + private const int BYTE_BITS = 8; + + #region CountBits + public static unsafe int CountBits8(T bits) where T : unmanaged + { + return CountBits((uint)*(byte*)&bits); + } + public static unsafe int CountBits16(T bits) where T : unmanaged + { + return CountBits((uint)*(ushort*)&bits); + } + + public static unsafe int CountBits32(T bits) where T : unmanaged + { + return CountBits(*(uint*)&bits); + } + public static unsafe int CountBits(float bits) + { + return CountBits32(bits); + } + public static unsafe int CountBits(int bits) + { + return CountBits((uint)bits); + } + public static unsafe int CountBits(uint bits) + { + bits = bits - ((bits >> 1) & 0x55555555); + bits = (bits & 0x33333333) + ((bits >> 2) & 0x33333333); + return (int)(((bits + (bits >> 4) & 0xF0F0F0F) * 0x1010101) >> 24); + } + + public static unsafe int CountBits64(T bits) where T : unmanaged + { + return CountBits(*(ulong*)&bits); + } + public static unsafe int CountBits(double bits) + { + return CountBits64(bits); + } + public static unsafe int CountBits(long bits) + { + return CountBits((ulong)bits); + } + public static unsafe int CountBits(ulong bits) + { + bits = bits - ((bits >> 1) & 0x55555555_55555555); + bits = (bits & 0x33333333_33333333) + ((bits >> 2) & 0x33333333_33333333); + return (int)(((bits + (bits >> 4) & 0x0F0F0F0F_0F0F0F0F) * 0x01010101_01010101) >> (24 + 32)); + } + #endregion + + #region GetHighBitNumber + public static unsafe int GetHighBitNumber8(T bits) where T : unmanaged + { + return GetHighBitNumber(*(byte*)&bits); + } + public static int GetHighBitNumber(sbyte bits) + { + return GetHighBitNumber((byte)bits); + } + public static int GetHighBitNumber(byte bits) + { + if (bits == 0) + { + return -1; + } + int bit = 0; + if ((bits & 0xF0) != 0) + { + bits >>= 4; + bit |= 4; + } + if ((bits & 0xC) != 0) + { + bits >>= 2; + bit |= 2; + } + if ((bits & 0x2) != 0) + { + bit |= 1; + } + return bit; + } + + public static unsafe int GetHighBitNumber16(T bits) where T : unmanaged + { + return GetHighBitNumber(*(ushort*)&bits); + } + public static int GetHighBitNumber(short bits) + { + return GetHighBitNumber((ushort)bits); + } + public static int GetHighBitNumber(ushort bits) + { + if (bits == 0) + { + return -1; + } + int bit = 0; + if ((bits & 0xFF00) != 0) + { + bits >>= 8; + bit |= 8; + } + if ((bits & 0xF0) != 0) + { + bits >>= 4; + bit |= 4; + } + if ((bits & 0xC) != 0) + { + bits >>= 2; + bit |= 2; + } + if ((bits & 0x2) != 0) + { + bit |= 1; + } + return bit; + } + + public static unsafe int GetHighBitNumber32(T bits) where T : unmanaged + { + return GetHighBitNumber(*(uint*)&bits); + } + public static int GetHighBitNumber(float bits) + { + return GetHighBitNumber(*(uint*)&bits); + } + public static int GetHighBitNumber(int bits) + { + return GetHighBitNumber((uint)bits); + } + public static int GetHighBitNumber(uint bits) + { + if (bits == 0) + { + return -1; + } + int bit = 0; + if ((bits & 0xFFFF0000) != 0) + { + bits >>= 16; + bit |= 16; + } + if ((bits & 0xFF00) != 0) + { + bits >>= 8; + bit |= 8; + } + if ((bits & 0xF0) != 0) + { + bits >>= 4; + bit |= 4; + } + if ((bits & 0xC) != 0) + { + bits >>= 2; + bit |= 2; + } + if ((bits & 0x2) != 0) + { + bit |= 1; + } + return bit; + } + + public static unsafe int GetHighBitNumber64(T bits) where T : unmanaged + { + return GetHighBitNumber(*(ulong*)&bits); + } + public static int GetHighBitNumber(double bits) + { + return GetHighBitNumber(*(ulong*)&bits); + } + public static int GetHighBitNumber(long bits) + { + return GetHighBitNumber((ulong)bits); + } + public static int GetHighBitNumber(ulong bits) + { + if (bits == 0) + { + return -1; + } + int bit = 0; + if ((bits & 0xFFFFFFFF00000000) != 0) + { + bits >>= 32; + bit |= 32; + } + if ((bits & 0xFFFF0000) != 0) + { + bits >>= 16; + bit |= 16; + } + if ((bits & 0xFF00) != 0) + { + bits >>= 8; + bit |= 8; + } + if ((bits & 0xF0) != 0) + { + bits >>= 4; + bit |= 4; + } + if ((bits & 0xC) != 0) + { + bits >>= 2; + bit |= 2; + } + if ((bits & 0x2) != 0) + { + bit |= 1; + } + return bit; + } + #endregion + + #region GetBitNumbers + public static unsafe int[] GetBitNumbers32(T bits) where T : unmanaged + { + return GetBitNumbers(*(uint*)&bits); + } + public static unsafe int[] GetBitNumbers(float bits) + { + return GetBitNumbers(*(uint*)&bits); + } + public static int[] GetBitNumbers(int bits) + { + return GetBitNumbers((uint)bits); + } + public static int[] GetBitNumbers(uint bits) + { + int[] result = new int[CountBits(bits)]; + for (int i = 0; i < result.Length; i++) + { + int number = GetHighBitNumber(bits); + result[i] = number; + bits ^= 1u << number; + } + return result; + } + + public static unsafe int GetBitNumbersNoAlloc32(T bits, ref int[] numbers) where T : unmanaged + { + return GetBitNumbersNoAlloc(*(uint*)&bits, ref numbers); + } + public static unsafe int GetBitNumbersNoAlloc(float bits, ref int[] numbers) + { + return GetBitNumbersNoAlloc(*(uint*)&bits, ref numbers); + } + public static int GetBitNumbersNoAlloc(int bits, ref int[] numbers) + { + return GetBitNumbersNoAlloc((uint)bits, ref numbers); + } + public static int GetBitNumbersNoAlloc(uint bits, ref int[] numbers) + { + int iMax = CountBits(bits); + if (iMax >= numbers.Length) + Array.Resize(ref numbers, iMax); + for (int i = 0; i < iMax; i++) + { + int number = GetHighBitNumber(bits); + numbers[i] = number; + bits ^= 1u << number; + } + return iMax; + } + public static unsafe void GetBitNumbersNoAlloc32(T bits, List numbers) where T : unmanaged + { + GetBitNumbersNoAlloc(*(uint*)&bits, numbers); + } + public static unsafe void GetBitNumbersNoAlloc(float bits, List numbers) + { + GetBitNumbersNoAlloc(*(uint*)&bits, numbers); + } + public static void GetBitNumbersNoAlloc(int bits, List numbers) + { + GetBitNumbersNoAlloc((uint)bits, numbers); + } + public static void GetBitNumbersNoAlloc(uint bits, List numbers) + { + numbers.Clear(); + int iMax = CountBits(bits); + for (int i = 0; i < iMax; i++) + { + int number = GetHighBitNumber(bits); + numbers[i] = number; + bits ^= 1u << number; + } + } + + + + public static unsafe int[] GetBitNumbers64(T bits) where T : unmanaged + { + return GetBitNumbers(*(ulong*)&bits); + } + public static unsafe int[] GetBitNumbers(double bits) + { + return GetBitNumbers(*(ulong*)&bits); + } + public static int[] GetBitNumbers(long bits) + { + return GetBitNumbers((ulong)bits); + } + public static int[] GetBitNumbers(ulong bits) + { + int[] result = new int[CountBits(bits)]; + for (int i = 0; i < result.Length; i++) + { + int number = GetHighBitNumber(bits); + result[i] = number; + bits ^= 1LU << number; + } + return result; + } + + public static unsafe int GetBitNumbersNoAlloc64(T bits, ref int[] numbers) where T : unmanaged + { + return GetBitNumbersNoAlloc(*(ulong*)&bits, ref numbers); + } + public static unsafe int GetBitNumbersNoAlloc(double bits, ref int[] numbers) + { + return GetBitNumbersNoAlloc(*(ulong*)&bits, ref numbers); + } + public static int GetBitNumbersNoAlloc(long bits, ref int[] numbers) + { + return GetBitNumbersNoAlloc((ulong)bits, ref numbers); + } + public static int GetBitNumbersNoAlloc(ulong bits, ref int[] numbers) + { + int iMax = CountBits(bits); + if (iMax >= numbers.Length) + Array.Resize(ref numbers, iMax); + for (int i = 0; i < iMax; i++) + { + int number = GetHighBitNumber(bits); + numbers[i] = number; + bits ^= 1u << number; + } + return iMax; + } + public static unsafe void GetBitNumbersNoAlloc64(T bits, List numbers) where T : unmanaged + { + GetBitNumbersNoAlloc(*(ulong*)&bits, numbers); + } + public static unsafe void GetBitNumbersNoAlloc(double bits, List numbers) + { + GetBitNumbersNoAlloc(*(ulong*)&bits, numbers); + } + public static void GetBitNumbersNoAlloc(long bits, List numbers) + { + GetBitNumbersNoAlloc((ulong)bits, numbers); + } + public static void GetBitNumbersNoAlloc(ulong bits, List numbers) + { + numbers.Clear(); + int iMax = CountBits(bits); + for (int i = 0; i < iMax; i++) + { + int number = GetHighBitNumber(bits); + numbers[i] = number; + bits ^= 1u << number; + } + } + #endregion + + #region ToBitsString + public static string ToBitsString(T value, bool withSeparator) where T : unmanaged + { + return ToBitsStringInaternal(value, withSeparator ? BYTE_BITS : 0, DEFAULT_SEPARATOR); + } + public static string ToBitsString(T value, int separateRange) where T : unmanaged + { + return ToBitsStringInaternal(value, separateRange, DEFAULT_SEPARATOR); + } + public static string ToBitsString(T value, char separator = DEFAULT_SEPARATOR, int separateRange = BYTE_BITS) where T : unmanaged + { + return ToBitsStringInaternal(value, separateRange, separator); + } + private static unsafe string ToBitsStringInaternal(T value, int separateRange, char separator) where T : unmanaged + { + int size = sizeof(T); + int length = size * BYTE_BITS; + //byte* bytes = stackalloc byte[size / BYTE_BITS]; + byte* bytes = (byte*)&value; + char* str = stackalloc char[length]; + + for (int i = 0; i < length; i++) + str[length - i - 1] = (bytes[i / BYTE_BITS] & 1 << (i % BYTE_BITS)) > 0 ? '1' : '0'; + + if (separateRange > 0) + return Regex.Replace(new string(str, 0, length), ".{" + separateRange + "}", "$0" + separator + ""); + else + return new string(str, 0, length); + } + #endregion + + #region ParceBitString + public static ulong ToULong(string bitsString) + { + const int BIT_SIZE = 64; + ulong result = 0; + int stringMouse = 0; + for (int i = 0; i < BIT_SIZE && stringMouse < bitsString.Length; i++, stringMouse++) + { + char chr = bitsString[stringMouse]; + + if (chr == '1') + { + result |= (ulong)1 << (BIT_SIZE - i - 1); + continue; + } + + if (chr != '0') + { + i--; + continue; + } + } + return result; + } + + public static uint ToUInt(string bitsString) + { + const int BIT_SIZE = 32; + uint result = 0; + int stringMouse = 0; + for (int i = 0; i < BIT_SIZE && stringMouse < bitsString.Length; i++, stringMouse++) + { + char chr = bitsString[stringMouse]; + + if (chr == '1') + { + result |= (uint)1 << (BIT_SIZE - i - 1); + continue; + } + + if (chr != '0') + { + i--; + continue; + } + } + return result; + } + + public static ushort ToUShort(string bitsString) + { + const int BIT_SIZE = 16; + ushort result = 0; + int stringMouse = 0; + for (int i = 0; i < BIT_SIZE && stringMouse < bitsString.Length; i++, stringMouse++) + { + char chr = bitsString[stringMouse]; + + if (chr == '1') + { + result |= (ushort)(1 << (BIT_SIZE - i - 1)); + continue; + } + + if (chr != '0') + { + i--; + continue; + } + } + return result; + } + + public static byte ToByte(string bitsString) + { + const int BIT_SIZE = 8; + byte result = 0; + int stringMouse = 0; + for (int i = 0; i < BIT_SIZE && stringMouse < bitsString.Length; i++, stringMouse++) + { + char chr = bitsString[stringMouse]; + + if (chr == '1') + { + result |= (byte)(1 << (BIT_SIZE - i - 1)); + continue; + } + + if (chr != '0') + { + i--; + continue; + } + } + return result; + } + + public static bool ToBool(string bitsString) + { + byte result = ToByte(bitsString); + return *(bool*)&result; + } + public static short ToShort(string bitsString) => (short)ToUShort(bitsString); + public static int ToInt(string bitsString) => (int)ToUInt(bitsString); + public static long ToLong(string bitsString) => (long)ToULong(bitsString); + #endregion + + #region XorShift + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static int NextXorShiftState(int state) + { + unchecked { return (state << 13) ^ (state >> 17) ^ (state << 5); }; + } + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static uint NextXorShiftState(uint state) + { + unchecked { return (state << 13) ^ (state >> 17) ^ (state << 5); }; + } + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static long NextXorShiftState(long state) + { + const long m = 0x2545F491_4F6CDD1D; + unchecked { return ((state >> 13) ^ (state << 25) ^ (state >> 27)) * m; }; + } + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static ulong NextXorShiftState(ulong state) + { + const ulong m = 0x2545F491_4F6CDD1D; + unchecked { return ((state >> 13) ^ (state << 25) ^ (state >> 27)) * m; }; + } + #endregion + + #region Q32/64/s31/s63 To Float/Double + ///Fast operation: float result = (float)value / int.MaxValue. + ///-1.0f < x < 1.0f + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static unsafe float Qs31ToFloat(int value) + { + unchecked + { + if (value < 0) + { + uint bits = (((uint)value ^ uint.MaxValue) >> 8) | 0xBF80_0000; + return (*(float*)&bits) + 1f; + } + else + { + uint bits = (((uint)value) >> 8) | 0x3F80_0000; + return (*(float*)&bits) - 1f; + } + } + } + ///Fast operation: float result = (float)value / uint.MaxValue. + ///0.0f <= x < 1.0f + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static unsafe float Q32ToFloat(uint value) + { + unchecked + { + uint bits = (value >> 9) | 0x3F80_0000; + return (*(float*)&bits) - 1f; + } + } + ///Fast operation: double result = (double)value / long.MaxValue. + ///-1.0d < x < 1.0d + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static unsafe double Qs63ToDouble(long value) + { + unchecked + { + if (value < 0) + { + ulong bits = (((ulong)value ^ long.MaxValue) >> 11) | 0xBFF0_0000_0000_0000; + return (*(double*)&bits) + 1d; + } + else + { + ulong bits = (((ulong)value) >> 11) | 0x3FF0_0000_0000_0000; + return (*(double*)&bits) - 1d; + } + } + } + + ///Fast operation: double result = (double)value / ulong.MaxValue. + ///0.0d <= x < 1.0d + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static unsafe double Q64ToDouble(ulong value) + { + unchecked + { + ulong bits = (value >> 12) | 0x3FF0_0000_0000_0000; + return (*(double*)&bits) - 1d; + } + } + #endregion + + #region Other + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static TEnum AllFlags() where TEnum : unmanaged, Enum + { + return EnumCache.allFlags; + } + + private static class EnumCache where TEnum : unmanaged, Enum + { + public readonly static TEnum empty; + public readonly static TEnum allFlags; + public readonly static int valuesCount; + public readonly static int size; + + static EnumCache() + { + Array values = Enum.GetValues(typeof(TEnum)); + size = sizeof(TEnum); + + long result = 0; + valuesCount = values.Length; + for (int i = 0; i < valuesCount; i++) + { + result |= (long)values.GetValue(i); + } + ulong emptyBits = 0; + + allFlags = *(TEnum*)&result; + empty = *(TEnum*)&emptyBits; + return; + } + } + #endregion + } + + #region FindBitsResult removed + /* + public unsafe struct FindBitsResult8 : IEnumerable + { + private fixed byte _numbers[8]; + public readonly byte Count; + public int this[int index] + { + [MethodImpl(MethodImplOptions.AggressiveInlining)] + get => _numbers[index]; + } + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public FindBitsResult8(uint bits) + { + unchecked + { + Count = (byte)CountBits(bits); + for (int i = 0; i < Count; i++) + { + int number = GetHighBitNumber(bits); + _numbers[i] = (byte)number; + bits ^= 1u << number; + } + } + } + [MethodImpl(MethodImplOptions.AggressiveInlining)] + private static ReadOnlySpan CreateSpanInternal(FindBitsResult8 a) + { + return new ReadOnlySpan(a._numbers, a.Count); + } + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public ReadOnlySpan ToSpan() + { + return CreateSpanInternal(this); + } + [MethodImpl(MethodImplOptions.AggressiveInlining)] + private static FindBitsResultEnumerator GetEnumeratorInternal(FindBitsResult8 a) + { + return new FindBitsResultEnumerator(a._numbers, a.Count); + } + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public FindBitsResultEnumerator GetEnumerator() + { + return GetEnumeratorInternal(this); + } + IEnumerator IEnumerable.GetEnumerator() => GetEnumerator(); + IEnumerator IEnumerable.GetEnumerator() => GetEnumerator(); + } + + public unsafe struct FindBitsResult16 : IEnumerable + { + private fixed byte _numbers[16]; + public readonly byte Count; + public int this[int index] + { + [MethodImpl(MethodImplOptions.AggressiveInlining)] + get => _numbers[index]; + } + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public FindBitsResult16(uint bits) + { + unchecked + { + Count = (byte)CountBits(bits); + for (int i = 0; i < Count; i++) + { + int number = GetHighBitNumber(bits); + _numbers[i] = (byte)number; + bits ^= 1u << number; + } + } + } + [MethodImpl(MethodImplOptions.AggressiveInlining)] + private static ReadOnlySpan CreateSpanInternal(FindBitsResult16 a) + { + return new ReadOnlySpan(a._numbers, a.Count); + } + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public ReadOnlySpan ToSpan() + { + return CreateSpanInternal(this); + } + [MethodImpl(MethodImplOptions.AggressiveInlining)] + private static FindBitsResultEnumerator GetEnumeratorInternal(FindBitsResult16 a) + { + return new FindBitsResultEnumerator(a._numbers, a.Count); + } + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public FindBitsResultEnumerator GetEnumerator() + { + return GetEnumeratorInternal(this); + } + IEnumerator IEnumerable.GetEnumerator() => GetEnumerator(); + IEnumerator IEnumerable.GetEnumerator() => GetEnumerator(); + } + + public unsafe struct FindBitsResult32 : IEnumerable + { + public fixed byte _numbers[32]; + public readonly byte Count; + public int this[int index] + { + [MethodImpl(MethodImplOptions.AggressiveInlining)] + get => _numbers[index]; + } + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public FindBitsResult32(uint bits) + { + unchecked + { + Count = (byte)CountBits(bits); + for (int i = 0; i < Count; i++) + { + int number = GetHighBitNumber(bits); + _numbers[i] = (byte)number; + bits ^= 1u << number; + } + } + } + [MethodImpl(MethodImplOptions.AggressiveInlining)] + private static ReadOnlySpan CreateSpanInternal(FindBitsResult32 a) + { + return new ReadOnlySpan(a._numbers, a.Count); + } + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public ReadOnlySpan ToSpan() + { + return CreateSpanInternal(this); + } + [MethodImpl(MethodImplOptions.AggressiveInlining)] + private static FindBitsResultEnumerator GetEnumeratorInternal(FindBitsResult32 a) + { + return new FindBitsResultEnumerator(a._numbers, a.Count); + } + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public FindBitsResultEnumerator GetEnumerator() + { + return GetEnumeratorInternal(this); + } + IEnumerator IEnumerable.GetEnumerator() => GetEnumerator(); + IEnumerator IEnumerable.GetEnumerator() => GetEnumerator(); + } + + public unsafe struct FindBitsResult64 : IEnumerable + { + private fixed byte _numbers[64]; + public readonly byte Count; + public int this[int index] + { + [MethodImpl(MethodImplOptions.AggressiveInlining)] + get => _numbers[index]; + } + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public FindBitsResult64(ulong bits) + { + unchecked + { + Count = (byte)CountBits(bits); + for (int i = 0; i < Count; i++) + { + int number = GetHighBitNumber(bits); + _numbers[i] = (byte)number; + bits ^= 1u << number; + } + } + } + [MethodImpl(MethodImplOptions.AggressiveInlining)] + private static ReadOnlySpan CreateSpanInternal(FindBitsResult64 a) + { + return new ReadOnlySpan(a._numbers, a.Count); + } + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public ReadOnlySpan ToSpan() + { + return CreateSpanInternal(this); + } + [MethodImpl(MethodImplOptions.AggressiveInlining)] + private static FindBitsResultEnumerator GetEnumeratorInternal(FindBitsResult64 a) + { + return new FindBitsResultEnumerator(a._numbers, a.Count); + } + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public FindBitsResultEnumerator GetEnumerator() + { + return GetEnumeratorInternal(this); + } + IEnumerator IEnumerable.GetEnumerator() => GetEnumerator(); + IEnumerator IEnumerable.GetEnumerator() => GetEnumerator(); + } + + public unsafe struct FindBitsResultEnumerator : IEnumerator + { + private byte* _numbers; + private byte _count; + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public FindBitsResultEnumerator(byte* numbers, byte count) + { + _numbers = numbers; + _count = count; + } + public byte Current + { + [MethodImpl(MethodImplOptions.AggressiveInlining)] + get + { + return *_numbers; + } + } + object IEnumerator.Current => Current; + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public bool MoveNext() + { + return _count-- > 0; + } + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public void Dispose() { } + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public void Reset() { } + } + */ + #endregion + + #region FindBitsIterator + public struct FindBitsIterator8 : IEnumerable + { + private Enumerator _enumerator; + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public FindBitsIterator8(sbyte bits) + { + _enumerator = new Enumerator((byte)bits); + } + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public FindBitsIterator8(byte bits) + { + _enumerator = new Enumerator(bits); + } + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public Enumerator GetEnumerator() + { + return _enumerator; + } + IEnumerator IEnumerable.GetEnumerator() => GetEnumerator(); + IEnumerator IEnumerable.GetEnumerator() => GetEnumerator(); + public struct Enumerator : IEnumerator + { + private uint _bits; + private int _count; + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public Enumerator(byte bits) + { + _count = CountBits(bits); + _bits = bits; + } + public int Current + { + [MethodImpl(MethodImplOptions.AggressiveInlining)] + get + { + int number = GetHighBitNumber((byte)_bits); + _bits ^= 1u << number; + return number; + } + } + object IEnumerator.Current => Current; + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public bool MoveNext() + { + return _count-- > 0; + } + [MethodImpl(MethodImplOptions.AggressiveInlining)] + void IDisposable.Dispose() { } + [MethodImpl(MethodImplOptions.AggressiveInlining)] + void IEnumerator.Reset() { } + } + } + + public struct FindBitsIterator16 : IEnumerable + { + private Enumerator _enumerator; + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public FindBitsIterator16(short bits) + { + _enumerator = new Enumerator((ushort)bits); + } + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public FindBitsIterator16(ushort bits) + { + _enumerator = new Enumerator(bits); + } + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public Enumerator GetEnumerator() + { + return _enumerator; + } + IEnumerator IEnumerable.GetEnumerator() => GetEnumerator(); + IEnumerator IEnumerable.GetEnumerator() => GetEnumerator(); + public struct Enumerator : IEnumerator + { + private uint _bits; + private int _count; + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public Enumerator(ushort bits) + { + _count = CountBits(bits); + _bits = bits; + } + public int Current + { + [MethodImpl(MethodImplOptions.AggressiveInlining)] + get + { + int number = GetHighBitNumber((ushort)_bits); + _bits ^= 1u << number; + return number; + } + } + object IEnumerator.Current => Current; + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public bool MoveNext() + { + return _count-- > 0; + } + [MethodImpl(MethodImplOptions.AggressiveInlining)] + void IDisposable.Dispose() { } + [MethodImpl(MethodImplOptions.AggressiveInlining)] + void IEnumerator.Reset() { } + } + } + + public struct FindBitsIterator32 : IEnumerable + { + private Enumerator _enumerator; + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public FindBitsIterator32(int bits) + { + _enumerator = new Enumerator((uint)bits); + } + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public FindBitsIterator32(uint bits) + { + _enumerator = new Enumerator(bits); + } + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public Enumerator GetEnumerator() + { + return _enumerator; + } + IEnumerator IEnumerable.GetEnumerator() => GetEnumerator(); + IEnumerator IEnumerable.GetEnumerator() => GetEnumerator(); + public struct Enumerator : IEnumerator + { + private uint _bits; + private int _count; + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public Enumerator(uint bits) + { + _count = CountBits(bits); + _bits = bits; + } + public int Current + { + [MethodImpl(MethodImplOptions.AggressiveInlining)] + get + { + int number = GetHighBitNumber(_bits); + _bits ^= 1u << number; + return number; + } + } + object IEnumerator.Current => Current; + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public bool MoveNext() + { + return _count-- > 0; + } + [MethodImpl(MethodImplOptions.AggressiveInlining)] + void IDisposable.Dispose() { } + [MethodImpl(MethodImplOptions.AggressiveInlining)] + void IEnumerator.Reset() { } + } + } + + public struct FindBitsIterator64 : IEnumerable + { + private Enumerator _enumerator; + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public FindBitsIterator64(long bits) + { + _enumerator = new Enumerator((ulong)bits); + } + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public FindBitsIterator64(ulong bits) + { + _enumerator = new Enumerator(bits); + } + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public Enumerator GetEnumerator() + { + return _enumerator; + } + IEnumerator IEnumerable.GetEnumerator() => GetEnumerator(); + IEnumerator IEnumerable.GetEnumerator() => GetEnumerator(); + public struct Enumerator : IEnumerator + { + private ulong _bits; + private int _count; + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public Enumerator(ulong bits) + { + _count = CountBits(bits); + _bits = bits; + } + public int Current + { + [MethodImpl(MethodImplOptions.AggressiveInlining)] + get + { + int number = GetHighBitNumber(_bits); + _bits ^= 1u << number; + return number; + } + } + object IEnumerator.Current => Current; + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public bool MoveNext() + { + return _count-- > 0; + } + [MethodImpl(MethodImplOptions.AggressiveInlining)] + void IDisposable.Dispose() { } + [MethodImpl(MethodImplOptions.AggressiveInlining)] + void IEnumerator.Reset() { } + } + } + #endregion +} \ No newline at end of file From b2414ec765a60356ce3dd0c822de7d99845e3045 Mon Sep 17 00:00:00 2001 From: Mikhail <99481254+DCFApixels@users.noreply.github.com> Date: Mon, 8 Jan 2024 13:39:38 +0800 Subject: [PATCH 10/11] fixes for support unity 2020.1.x --- src/Collections/EcsSpan.cs | 2 +- src/DataInterfaces.cs | 2 +- src/EcsMask.cs.meta | 11 +++++++++++ src/EcsRunner.cs | 12 ++++++------ src/Utils/ArraySortHalperX.cs.meta | 11 +++++++++++ src/Utils/BitsUtility.cs.meta | 11 +++++++++++ src/Utils/ReadOnlySpanDummy.cs | 14 ++++++++++++++ src/Utils/UnsafeArray.cs.meta | 11 +++++++++++ 8 files changed, 66 insertions(+), 8 deletions(-) create mode 100644 src/EcsMask.cs.meta create mode 100644 src/Utils/ArraySortHalperX.cs.meta create mode 100644 src/Utils/BitsUtility.cs.meta create mode 100644 src/Utils/UnsafeArray.cs.meta diff --git a/src/Collections/EcsSpan.cs b/src/Collections/EcsSpan.cs index 579d916..f45d97c 100644 --- a/src/Collections/EcsSpan.cs +++ b/src/Collections/EcsSpan.cs @@ -28,7 +28,7 @@ namespace DCFApixels.DragonECS [MethodImpl(MethodImplOptions.AggressiveInlining)] get => _values.Length; } - public readonly int this[int index] + public int this[int index] { [MethodImpl(MethodImplOptions.AggressiveInlining)] get => _values[index]; diff --git a/src/DataInterfaces.cs b/src/DataInterfaces.cs index ef4ef76..5255dd5 100644 --- a/src/DataInterfaces.cs +++ b/src/DataInterfaces.cs @@ -75,7 +75,7 @@ namespace DCFApixels.DragonECS internal sealed class ComponentResetHandler : IEcsComponentReset where T : IEcsComponentReset { - private T _fakeInstnace; + private T _fakeInstnace = default; [MethodImpl(MethodImplOptions.AggressiveInlining)] public void Reset(ref T component) => _fakeInstnace.Reset(ref component); } diff --git a/src/EcsMask.cs.meta b/src/EcsMask.cs.meta new file mode 100644 index 0000000..c16093c --- /dev/null +++ b/src/EcsMask.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: e1975c74e7ab50e4dba6bf01902e26e9 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/src/EcsRunner.cs b/src/EcsRunner.cs index e229c4d..46bdd3f 100644 --- a/src/EcsRunner.cs +++ b/src/EcsRunner.cs @@ -138,12 +138,12 @@ namespace DCFApixels.DragonECS throw new Exception(); } - Type GetRunnerBaseType(Type type) + Type GetRunnerBaseType(Type inType) { - if (type.IsGenericType && type.GetGenericTypeDefinition() == typeof(EcsRunner<>)) - return type; - if (type.BaseType != null) - return GetRunnerBaseType(type.BaseType); + if (inType.IsGenericType && inType.GetGenericTypeDefinition() == typeof(EcsRunner<>)) + return inType; + if (inType.BaseType != null) + return GetRunnerBaseType(inType.BaseType); return null; } Type baseType = GetRunnerBaseType(type); @@ -291,7 +291,7 @@ namespace DCFApixels.DragonECS name = Regex.Replace(name, @"\bEcs|Process\b", ""); if (Regex.IsMatch(name, "`\\w{1,}$")) { - var s = name.Split("`"); + var s = name.Split('`'); name = s[0] + $"<{s[1]}>"; } _processes.Add(type, new ProcessInterface(type, name)); diff --git a/src/Utils/ArraySortHalperX.cs.meta b/src/Utils/ArraySortHalperX.cs.meta new file mode 100644 index 0000000..0805127 --- /dev/null +++ b/src/Utils/ArraySortHalperX.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: a591de1858028504d819333121bfddd6 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/src/Utils/BitsUtility.cs.meta b/src/Utils/BitsUtility.cs.meta new file mode 100644 index 0000000..e87dd37 --- /dev/null +++ b/src/Utils/BitsUtility.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: 06a40460050b1ef4fa037b3a3cac9a8b +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/src/Utils/ReadOnlySpanDummy.cs b/src/Utils/ReadOnlySpanDummy.cs index 508b375..43a79a6 100644 --- a/src/Utils/ReadOnlySpanDummy.cs +++ b/src/Utils/ReadOnlySpanDummy.cs @@ -68,12 +68,14 @@ namespace DCFApixels.DragonECS #endregion #region Object +#pragma warning disable CS0809 // [Obsolete("Equals() on ReadOnlySpan will always throw an exception. Use the equality operator instead.")] [EditorBrowsable(EditorBrowsableState.Never)] public override bool Equals(object obj) => throw new NotSupportedException(); [Obsolete("GetHashCode() on ReadOnlySpan will always throw an exception.")] [EditorBrowsable(EditorBrowsableState.Never)] public override int GetHashCode() => throw new NotSupportedException(); +#pragma warning restore CS0809 // public override string ToString() { //if (typeof(T) == typeof(char)) @@ -154,6 +156,18 @@ namespace DCFApixels.DragonECS // } // return retVal; //} + public void CopyTo(T[] array) + { + if (_length > array.Length) + { + throw new ArgumentOutOfRangeException(); + } + + for (int i = 0; i < _length; i++) + { + array[i] = _array[i]; + } + } [MethodImpl(MethodImplOptions.AggressiveInlining)] public ReadOnlySpan Slice(int start) diff --git a/src/Utils/UnsafeArray.cs.meta b/src/Utils/UnsafeArray.cs.meta new file mode 100644 index 0000000..fbf882a --- /dev/null +++ b/src/Utils/UnsafeArray.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: 5d8f3c5034bb040478b60d7421a38ef8 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: From 5de4ce5899e6e237eb19bfd26a3d2f0a23fe6232 Mon Sep 17 00:00:00 2001 From: Mikhail <99481254+DCFApixels@users.noreply.github.com> Date: Mon, 8 Jan 2024 13:40:46 +0800 Subject: [PATCH 11/11] up version to 0.8.5 --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index 559235e..911e90e 100644 --- a/package.json +++ b/package.json @@ -8,7 +8,7 @@ "displayName": "DragonECS", "description": "C# Entity Component System Framework", "unity": "2020.3", - "version": "0.8.1", + "version": "0.8.5", "repository": { "type": "git", "url": "https://github.com/DCFApixels/DragonECS.git"