diff --git a/README-RU.md b/README-RU.md index a29d32c..5cf9ea4 100644 --- a/README-RU.md +++ b/README-RU.md @@ -1,5 +1,5 @@

- +

@@ -33,4 +33,41 @@ -
\ No newline at end of file +
+ +Реализация связи сущностей в виде графа, где дугами выступают связывающие сущности. + +> [!WARNING] +> Проект в стадии разработки. API может меняться. + +# Оглавление +- [Установка](#установка) +- [Инициализация](#инициализация) + +
+ +# Установка +Семантика версионирования - [Открыть](https://gist.github.com/DCFApixels/e53281d4628b19fe5278f3e77a7da9e8#file-dcfapixels_versioning_ru-md) +## Окружение +Обязательные требования: ++ Зависимость: [DragonECS](https://github.com/DCFApixels/DragonECS) ++ Минимальная версия C# 7.3; + +Опционально: ++ Игровые движки с C#: Unity, Godot, MonoGame и т.д. + +Протестировано: ++ **Unity:** Минимальная версия 2020.1.0; + +## Установка для Unity +* ### Unity-модуль +Поддерживается установка в виде Unity-модуля в при помощи добавления git-URL [в PackageManager](https://docs.unity3d.com/2023.2/Documentation/Manual/upm-ui-giturl.html) или ручного добавления в `Packages/manifest.json`: +``` +https://github.com/DCFApixels/DragonECS-Graphs.git +``` +* ### В виде исходников +Пакет так же может быть добавлен в проект в виде исходников. + +
+ +# Инициализация diff --git a/README.md b/README.md index 10a75c3..ed10d5d 100644 --- a/README.md +++ b/README.md @@ -34,3 +34,36 @@
+ +Text Text Text Text Text Text Text Text Text Text Text Text Text Text Text Text Text Text Text Text Text Text Text Text Text Text Text Text Text Text Text Text Text Text Text Text Text Text Text Text Text Text Text Text Text Text Text Text Text Text Text Text Text Text + +> [!WARNING] +> The project is a work in progress, API may change. +> +> While the English version of the README is incomplete, you can view the [Russian version](https://github.com/DCFApixels/DragonECS-Graphs/blob/main/README-RU.md). + +
+ +# Installation +Versioning semantics - [Open](https://gist.github.com/DCFApixels/e53281d4628b19fe5278f3e77a7da9e8#file-dcfapixels_versioning_ru-md) +## Environment +Requirements: ++ Dependency: [DragonECS](https://github.com/DCFApixels/DragonECS) ++ Minimum version of C# 7.3; + +Optional: ++ Game engines with C#: Unity, Godot, MonoGame, etc. + +Tested with: ++ **Unity:** Minimum version 2020.1.0; + +## Unity Installation +* ### Unity Package +The package can be installed as a Unity package by adding the Git URL [in the PackageManager](https://docs.unity3d.com/2023.2/Documentation/Manual/upm-ui-giturl.html) or manually adding it to `Packages/manifest.json`: +``` +https://github.com/DCFApixels/DragonECS-Graphs.git +``` +* ### Source Code +The package can also be added to the project as source code. + +
diff --git a/src/Executors/JoinToSubGraphExecutor.cs b/src/Executors/JoinToSubGraphExecutor.cs index f29fd0d..876727c 100644 --- a/src/Executors/JoinToSubGraphExecutor.cs +++ b/src/Executors/JoinToSubGraphExecutor.cs @@ -90,11 +90,11 @@ namespace DCFApixels.DragonECS.Graphs.Internal if (Mask.IsEmpty || _versionsChecker.CheckAndNext() == false) { _filteredAllEntitiesCount = _iterator.IterateTo(World.Entities, ref _filteredAllEntities); - //Подготовка массивов - if (_sourceEntities.Length < _filteredAllEntitiesCount * 2) + if (_sourceEntities.Length < _graph.World.Capacity * 2) { - _sourceEntities = new int[_filteredAllEntitiesCount * 2]; + _sourceEntities = new int[_graph.World.Capacity * 2]; } + //Подготовка массивов } @@ -103,9 +103,9 @@ namespace DCFApixels.DragonECS.Graphs.Internal _currentFilteredEntitiesCount = _filteredAllEntitiesCount; //Подготовка массивов - if (_targetWorldCapacity < World.Capacity) + if (_targetWorldCapacity < _graph.World.Capacity) { - _targetWorldCapacity = World.Capacity; + _targetWorldCapacity = _graph.World.Capacity; _linkedListSourceHeads = new LinkedListHead[_targetWorldCapacity]; //_startEntities = new int[_targetWorldCapacity]; } diff --git a/src/Internal/SparseMatrix.cs b/src/Internal/SparseMatrix.cs index 60ed27f..415af23 100644 --- a/src/Internal/SparseMatrix.cs +++ b/src/Internal/SparseMatrix.cs @@ -1,6 +1,7 @@ using System; using System.Runtime.CompilerServices; using System.Runtime.InteropServices; +using System.Threading; using TValue = System.Int32; namespace DCFApixels.DragonECS.Graphs.Internal @@ -10,12 +11,13 @@ namespace DCFApixels.DragonECS.Graphs.Internal public const int MIN_CAPACITY_BITS_OFFSET = 4; public const int MIN_CAPACITY = 1 << MIN_CAPACITY_BITS_OFFSET; - - private const int MAX_CHAIN_LENGTH = 5; + private const int CHAIN_LENGTH_THRESHOLD = 5; + private const float CHAIN_LENGTH_THRESHOLD_CAPCITY_THRESHOLD = 0.7f; private UnsafeArray _buckets; private UnsafeArray _entries; private int _capacity; + private int _count_Threshold; private int _count; @@ -54,7 +56,7 @@ namespace DCFApixels.DragonECS.Graphs.Internal _freeList = 0; _freeCount = 0; - _capacity = minCapacity; + SetCapacity(minCapacity); } #endregion @@ -69,7 +71,7 @@ namespace DCFApixels.DragonECS.Graphs.Internal Throw.ArgumentException("Has(x, y) is true"); } #endif - int targetBucket = key.yHash & _modBitMask; + int targetBucket = key.YHash & _modBitMask; AddInternal(key, targetBucket, value); } [MethodImpl(MethodImplOptions.AggressiveInlining)] @@ -80,7 +82,7 @@ namespace DCFApixels.DragonECS.Graphs.Internal { return false; } - int targetBucket = key.yHash & _modBitMask; + int targetBucket = key.YHash & _modBitMask; AddInternal(key, targetBucket, value); return true; } @@ -88,7 +90,7 @@ namespace DCFApixels.DragonECS.Graphs.Internal public void Set(int x, int y, TValue value) { Key key = Key.FromXY(x, y); - int targetBucket = key.yHash & _modBitMask; + int targetBucket = key.YHash & _modBitMask; for (int i = _buckets[targetBucket].index; i >= 0; i = _entries[i].next) { @@ -109,9 +111,11 @@ namespace DCFApixels.DragonECS.Graphs.Internal if (_count == _capacity) { Resize(); - targetBucket = key.yHash & _modBitMask; + // обновляем под новое значение _modBitMask + targetBucket = key.YHash & _modBitMask; } - index = _count++; + index = Interlocked.Increment(ref _count); + //index = _count++; } else { @@ -130,10 +134,13 @@ namespace DCFApixels.DragonECS.Graphs.Internal entry.next = basket.index; entry.key = key; entry.value = value; - basket.count++; + Interlocked.Increment(ref basket.count); + //basket.count++; basket.index = index; - if (basket.count >= MAX_CHAIN_LENGTH && Count / Capacity >= 0.7f) + if (basket.count >= CHAIN_LENGTH_THRESHOLD && + _count > _count_Threshold) + //_count / _capacity >= CHAIN_LENGTH_THRESHOLD_CAPCITY_THRESHOLD) { Resize(); } @@ -145,7 +152,7 @@ namespace DCFApixels.DragonECS.Graphs.Internal private int FindEntry(int x, int y) { Key key = Key.FromXY(x, y); - for (int i = _buckets[key.yHash & _modBitMask].index; i >= 0; i = _entries[i].next) + for (int i = _buckets[key.YHash & _modBitMask].index; i >= 0; i = _entries[i].next) { if (_entries[i].key == key) { @@ -157,7 +164,7 @@ namespace DCFApixels.DragonECS.Graphs.Internal [MethodImpl(MethodImplOptions.AggressiveInlining)] private int FindEntry(Key key) { - for (int i = _buckets[key.yHash & _modBitMask].index; i >= 0; i = _entries[i].next) + for (int i = _buckets[key.YHash & _modBitMask].index; i >= 0; i = _entries[i].next) { if (_entries[i].key == key) { @@ -202,7 +209,7 @@ namespace DCFApixels.DragonECS.Graphs.Internal public bool TryDel(int x, int y) { Key key = Key.FromXY(x, y); - int targetBucket = key.yHash & _modBitMask; + int targetBucket = key.YHash & _modBitMask; ref Basket basket = ref _buckets[targetBucket]; int last = -1; @@ -269,10 +276,10 @@ namespace DCFApixels.DragonECS.Graphs.Internal UnsafeArray newEntries = UnsafeArray.Resize(_entries, newSize); for (int i = 0; i < _count; i++) { - if (newEntries[i].key.x >= 0) + if (newEntries[i].key.X >= 0) { ref Entry entry = ref newEntries[i]; - ref Basket basket = ref newBuckets[entry.key.yHash & _modBitMask]; + ref Basket basket = ref newBuckets[entry.key.YHash & _modBitMask]; entry.next = basket.index; basket.index = i; basket.count++; @@ -282,7 +289,13 @@ namespace DCFApixels.DragonECS.Graphs.Internal _buckets = newBuckets; _entries = newEntries; + SetCapacity(newSize); + } + + private void SetCapacity(int newSize) + { _capacity = newSize; + _count_Threshold = (int)(_count_Threshold * CHAIN_LENGTH_THRESHOLD_CAPCITY_THRESHOLD); } [MethodImpl(MethodImplOptions.AggressiveInlining)] @@ -301,7 +314,7 @@ namespace DCFApixels.DragonECS.Graphs.Internal public int next; // Index of next entry, -1 if last public Key key; public TValue value; - public override string ToString() { return key.x == 0 ? "NULL" : $"{key} {value}"; } + public override string ToString() { return key.X == 0 ? "NULL" : $"{key} {value}"; } } [StructLayout(LayoutKind.Sequential, Pack = 4, Size = 8)] @@ -311,54 +324,63 @@ namespace DCFApixels.DragonECS.Graphs.Internal public int index; public int count; [MethodImpl(MethodImplOptions.AggressiveInlining)] - public Basket(int index, int length) + public Basket(int index, int count) { this.index = index; - this.count = length; + this.count = count; } public override string ToString() { return index < 0 ? "NULL" : $"{index} {count}"; } } - [StructLayout(LayoutKind.Sequential, Pack = 4, Size = 8)] + [StructLayout(LayoutKind.Explicit, Pack = 4, Size = 8)] public readonly struct Key : IEquatable { public static readonly Key Null = new Key(-1, 0); - public readonly int x; - public readonly int yHash; + + [FieldOffset(0)] + public readonly long Full; + [FieldOffset(0)] + public readonly int X; + [FieldOffset(4)] + public readonly int YHash; + [MethodImpl(MethodImplOptions.AggressiveInlining)] - private Key(int x, int yHash) + private Key(int x, int yHash) : this() { - this.x = x; - this.yHash = yHash; + this.X = x; + this.YHash = yHash; } [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static Key FromXY(int x, int y) + public static unsafe Key FromXY(int x, int y) { unchecked { - return new Key(x, x ^ y ^ XXX(y)); + return new Key(x, x ^ y ^ Mixing(y)); } } - private static int XXX(int x) + [MethodImpl(MethodImplOptions.AggressiveInlining)] + private static int Mixing(int x) { - x *= 3571; - x ^= x << 13; - x ^= x >> 17; - return x; + unchecked + { + x *= 3571; + x ^= x << 13; + x ^= x >> 17; + return x; + } } [MethodImpl(MethodImplOptions.AggressiveInlining)] - internal static bool EqualsInFind(Key a, Key b) { return a.x == b.x; } + internal static bool EqualsInFind(Key a, Key b) { return a.X == b.X; } [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static bool operator ==(Key a, Key b) { return a.x == b.x && a.yHash == b.yHash; } + public static bool operator ==(Key a, Key b) { return a.X == b.X && a.YHash == b.YHash; } [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static bool operator !=(Key a, Key b) { return a.x != b.x || a.yHash != b.yHash; } + public static bool operator !=(Key a, Key b) { return a.X != b.X || a.YHash != b.YHash; } [MethodImpl(MethodImplOptions.AggressiveInlining)] - public override int GetHashCode() { return yHash; } + public override int GetHashCode() { return YHash; } [MethodImpl(MethodImplOptions.AggressiveInlining)] public bool Equals(Key other) { return this == other; } public override bool Equals(object obj) { return obj is Key && Equals((Key)obj); } - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public override string ToString() { return $"({x}, {yHash})"; } + public override string ToString() { return $"({X}, {YHash})"; } } #endregion }