diff --git a/package.json b/package.json index e30473f..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.3", + "version": "0.8.5", "repository": { "type": "git", "url": "https://github.com/DCFApixels/DragonECS.git" diff --git a/src/Collections/EcsGroup.cs b/src/Collections/EcsGroup.cs index 51fd4ed..2909f45 100644 --- a/src/Collections/EcsGroup.cs +++ b/src/Collections/EcsGroup.cs @@ -1,7 +1,9 @@ using DCFApixels.DragonECS.Internal; +using DCFApixels.DragonECS.Utils; using System; using System.Collections; using System.Collections.Generic; +using System.ComponentModel; using System.Diagnostics; using System.Linq; using System.Runtime.CompilerServices; @@ -12,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; @@ -111,10 +113,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,12 +120,25 @@ 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 - { - public DebuggerProxy(EcsReadonlyGroup group) : base(group._source) { } - } + //internal class DebuggerProxy : EcsGroup.DebuggerProxy + //{ + // public DebuggerProxy(EcsReadonlyGroup group) : base(group._source) { } + //} #endregion } @@ -503,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)) @@ -710,7 +721,8 @@ namespace DCFApixels.DragonECS #region Other public override string ToString() { - return $"group{{{string.Join(", ", _dense.Skip(1).Take(_count).OrderBy(o => o))}}}"; + 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)] public int First() @@ -752,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 636703d..f45d97c 100644 --- a/src/Collections/EcsSpan.cs +++ b/src/Collections/EcsSpan.cs @@ -1,10 +1,13 @@ -using System; +using DCFApixels.DragonECS.Utils; +using System; using System.Collections.Generic; using System.ComponentModel; +using System.Diagnostics; using System.Runtime.CompilerServices; namespace DCFApixels.DragonECS { + [DebuggerTypeProxy(typeof(DebuggerProxy))] public readonly ref struct EcsSpan { private readonly int _worldID; @@ -34,7 +37,7 @@ namespace DCFApixels.DragonECS { [MethodImpl(MethodImplOptions.AggressiveInlining)] get => _values.IsEmpty; - } + } #endregion #region Constructors @@ -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 CollectionUtility.EntitiesToString(_values.ToArray(), "span"); + //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 DebuggerProxy(EcsSpan span) + { + _values = new int[span.Length]; + span._values.CopyTo(_values); + _worldID = span._worldID; + } + } #endregion } } 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/DataInterfaces.cs b/src/DataInterfaces.cs index f64125c..5255dd5 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); @@ -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); } @@ -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 9eab505..17c6369 100644 --- a/src/EcsAspect.cs +++ b/src/EcsAspect.cs @@ -1,27 +1,31 @@ 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 @@ -29,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 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); @@ -57,13 +57,37 @@ namespace DCFApixels.DragonECS newAspect = (EcsAspect)Activator.CreateInstance(typeof(TAspect)); newAspect.Init(builder); } - newAspect.source = world; - builder.End(out newAspect.mask); + newAspect._source = world; + builder.Build(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; } - #region Include/Exclude/Optional + #region Include/Exclude/Optional/Combine public sealed override TPool Include() { IncludeImplicit(typeof(TPool).GetGenericArguments()[0]); @@ -80,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 @@ -110,72 +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 = EcsMaskBit.FromID(id); - if (r.ContainsKey(bit.chankIndex)) - { - r[bit.chankIndex] = r[bit.chankIndex] | bit.mask; - } - else - { - r[bit.chankIndex] = bit.mask; - } - } - EcsMaskBit[] incMasks = r.Select(o => new EcsMaskBit(o.Key, o.Value)).ToArray(); - r.Clear(); - foreach (var id in maskExc) - { - var bit = EcsMaskBit.FromID(id); - if (r.ContainsKey(bit.chankIndex)) - { - r[bit.chankIndex] = r[bit.chankIndex] | bit.mask; - } - else - { - r[bit.chankIndex] = bit.mask; - } - } - EcsMaskBit[] excMasks = r.Select(o => new EcsMaskBit(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); - _world = null; - _inc = null; - _exc = null; + mask = _maskBuilder.Build(); } #region SupportReflectionHack @@ -194,10 +144,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) { @@ -205,6 +165,7 @@ namespace DCFApixels.DragonECS } #endregion + #region Combined private readonly struct Combined { public readonly EcsAspect aspect; @@ -215,6 +176,7 @@ namespace DCFApixels.DragonECS this.order = order; } } + #endregion } #region BuilderBase @@ -226,147 +188,19 @@ namespace DCFApixels.DragonECS } #endregion - #region Mask - 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; - } - 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 $"bit({chankIndex}, {mask})"; - } - } - - [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; - /// 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(); @@ -380,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; } @@ -402,58 +236,163 @@ 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 ref struct Enumerator + public unsafe ref struct Enumerator { + #region CountComparers + 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]; + } + } + #endregion + private ReadOnlySpan.Enumerator _span; - private readonly EcsMaskBit[] _incChunckMasks; - private readonly EcsMaskBit[] _excChunckMasks; private readonly int[][] _entitiesComponentMasks; - public Enumerator(EcsSpan span, EcsMask mask) + private static EcsMaskChunck* _preSortedIncBuffer; + private static EcsMaskChunck* _preSortedExcBuffer; + + private UnsafeArray _sortIncChunckBuffer; + private UnsafeArray _sortExcChunckBuffer; + + //private EcsAspect aspect; + + public unsafe Enumerator(EcsSpan span, EcsAspect aspect) { + //this.aspect = aspect; _span = span.GetEnumerator(); - _incChunckMasks = mask.incChunckMasks; - _excChunckMasks = mask.excChunckMasks; - _entitiesComponentMasks = span.World._entitiesComponentMasks; + _entitiesComponentMasks = aspect.World._entitiesComponentMasks; + _sortIncChunckBuffer = aspect._sortIncChunckBuffer; + _sortExcChunckBuffer = aspect._sortExcChunckBuffer; + #region Sort + UnsafeArray _sortIncBuffer = aspect._sortIncBuffer; + UnsafeArray _sortExcBuffer = aspect._sortExcBuffer; + int[] counts = aspect.World._poolComponentCounts; - + if (_preSortedIncBuffer == null) + { + _preSortedIncBuffer = UnmanagedArrayUtility.New(256); + _preSortedExcBuffer = UnmanagedArrayUtility.New(256); + } + + 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]; + 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 + } + } + } + + 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]; + 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 + } + } + } + #endregion } public int Current { [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; - EcsMaskBit bit; - for (int i = 0, iMax = _incChunckMasks.Length; i < iMax; i++) + for (int i = 0; i < _sortIncChunckBuffer.Length; i++) { - bit = _incChunckMasks[i]; + var bit = _sortIncChunckBuffer.ptr[i]; if ((_entitiesComponentMasks[e][bit.chankIndex] & bit.mask) != bit.mask) goto skip; } - for (int i = 0, iMax = _excChunckMasks.Length; i < iMax; i++) + for (int i = 0; i < _sortExcChunckBuffer.Length; i++) { - bit = _excChunckMasks[i]; + var bit = _sortExcChunckBuffer.ptr[i]; if ((_entitiesComponentMasks[e][bit.chankIndex] & bit.mask) > 0) goto skip; } diff --git a/src/EcsMask.cs b/src/EcsMask.cs new file mode 100644 index 0000000..f6c27b2 --- /dev/null +++ b/src/EcsMask.cs @@ -0,0 +1,376 @@ +using DCFApixels.DragonECS.Internal; +using System; +using System.Collections.Generic; +using System.Diagnostics; +using System.Linq; +using System.Runtime.CompilerServices; + +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)) + { + result = new EcsMask(_masks.Count, id, maskKey.inc, maskKey.exc); + _masks.Add(maskKey, result); + } + return result; + } + } + + [DebuggerTypeProxy(typeof(DebuggerProxy))] + public sealed class EcsMask : IEquatable + { + internal readonly int id; + internal readonly int worldID; + internal readonly EcsMaskChunck[] incChunckMasks; + 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 + CheckConstraints(inc, exc); +#endif + this.id = id; + this.inc = inc; + this.exc = exc; + this.worldID = worldID; + + incChunckMasks = MakeMaskChuncsArray(inc); + excChunckMasks = MakeMaskChuncsArray(exc); + } + + 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; + } + #endregion + + #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 +#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 int ID; + public readonly EcsWorld world; + private 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) + { + ID = mask.id; + 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 + + #region Builder + 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 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; + } + + 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); + + unchecked + { + 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); + } + } + } + + private readonly struct Combined + { + public readonly EcsMask mask; + public readonly int order; + public Combined(EcsMask mask, int order) + { + this.mask = mask; + this.order = order; + } + } + #endregion + } + + #region EcsMaskChunck + [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); + } + } + } + #endregion +} 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 ecb4b15..46bdd3f 100644 --- a/src/EcsRunner.cs +++ b/src/EcsRunner.cs @@ -138,12 +138,12 @@ namespace DCFApixels.DragonECS throw new Exception(); } - Type GetRunnerBaseType(Type typeX) + Type GetRunnerBaseType(Type inType) { - if (typeX.IsGenericType && typeX.GetGenericTypeDefinition() == typeof(EcsRunner<>)) - return typeX; - if (typeX.BaseType != null) - return GetRunnerBaseType(typeX.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("`".ToCharArray()); + var s = name.Split('`'); name = s[0] + $"<{s[1]}>"; } _processes.Add(type, new ProcessInterface(type, name)); 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 43ceec2..137665c 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++) @@ -141,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.pools.cs b/src/EcsWorld.pools.cs index f712602..06af5e4 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) + private void RegisterEntityComponent(int entityID, int componentTypeID, EcsMaskChunck 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) + private void UnregisterEntityComponent(int entityID, int componentTypeID, EcsMaskChunck maskBit) { - //_poolComponentCounts[componentTypeID]--; + _poolComponentCounts[componentTypeID]--; var count = --_componentCounts[entityID]; _entitiesComponentMasks[entityID][maskBit.chankIndex] &= ~maskBit.mask; @@ -133,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; } @@ -152,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/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); } 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/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/ArrayUtility.cs b/src/Utils/ArrayUtility.cs index 7ad4540..1a80faa 100644 --- a/src/Utils/ArrayUtility.cs +++ b/src/Utils/ArrayUtility.cs @@ -1,6 +1,9 @@ -using System.Runtime.CompilerServices; +using System; +using System.Collections; +using System.Collections.Generic; +using System.Linq; +using System.Runtime.CompilerServices; using System.Runtime.InteropServices; -using System; namespace DCFApixels.DragonECS.Utils { @@ -16,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)] @@ -24,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 @@ -36,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 @@ -51,4 +125,16 @@ namespace DCFApixels.DragonECS.Utils new IntPtr(Marshal.SizeOf(typeof(T)) * newCount))).ToPointer(); } } -} + + public static class CollectionUtility + { + 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 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 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 03a291a..a565316 100644 --- a/src/Utils/ReadOnlySpanDummy.cs +++ b/src/Utils/ReadOnlySpanDummy.cs @@ -157,6 +157,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]; + } + } public void CopyTo(T[] array) { 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; + } + } + } +} 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: