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] 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