mirror of
https://github.com/DCFApixels/DragonECS-Graphs.git
synced 2025-09-17 11:04:34 +08:00
update
This commit is contained in:
parent
e2b68d2b08
commit
cde2952bf9
@ -3,7 +3,7 @@ using System;
|
||||
|
||||
namespace DCFApixels.DragonECS
|
||||
{
|
||||
public static class EcsGraphExtensions
|
||||
public static class EntityGraphExtensions
|
||||
{
|
||||
private static EntityGraph[] _worldGraphs = new EntityGraph[4];
|
||||
|
@ -27,12 +27,15 @@ namespace DCFApixels.DragonECS
|
||||
private LinkedList _linkedList;
|
||||
private LinkedListHead[] _linkedListSourceHeads;
|
||||
|
||||
private int[] _startEntities;
|
||||
private int _startEntitiesCount;
|
||||
private int[] _sourceEntities;
|
||||
private int _sourceEntitiesCount;
|
||||
|
||||
private int _targetWorldCapacity = -1;
|
||||
private EcsProfilerMarker _executeMarker = new EcsProfilerMarker("Join");
|
||||
|
||||
public bool _isDestroyed = false;
|
||||
|
||||
|
||||
#region Properties
|
||||
public sealed override long Version
|
||||
{
|
||||
@ -62,13 +65,17 @@ namespace DCFApixels.DragonECS
|
||||
_versionsChecker = new WorldStateVersionsChecker(Mask);
|
||||
_linkedList = new OnlyAppendHeadLinkedList(World.Capacity);
|
||||
_linkedListSourceHeads = new LinkedListHead[World.Capacity];
|
||||
_sourceEntities = new int[World.Capacity * 2];
|
||||
World.AddListener(this);
|
||||
_graph = World.GetGraph();
|
||||
_iterator = Mask.GetIterator();
|
||||
}
|
||||
protected override void OnDestroy()
|
||||
{
|
||||
if (_isDestroyed) { return; }
|
||||
_isDestroyed = true;
|
||||
World.RemoveListener(this);
|
||||
_versionsChecker.Dispose();
|
||||
}
|
||||
#endregion
|
||||
|
||||
@ -79,24 +86,16 @@ namespace DCFApixels.DragonECS
|
||||
|
||||
World.ReleaseDelEntityBufferAllAuto();
|
||||
|
||||
if (Mask.IsEmpty)
|
||||
{
|
||||
_filteredAllEntitiesCount = World.Entities.ToArray(ref _filteredAllEntities);
|
||||
}
|
||||
else
|
||||
{
|
||||
if (_versionsChecker.CheckAndNext() == false)
|
||||
if (Mask.IsEmpty || _versionsChecker.CheckAndNext() == false)
|
||||
{
|
||||
_filteredAllEntitiesCount = _iterator.IterateTo(World.Entities, ref _filteredAllEntities);
|
||||
////Подготовка массивов
|
||||
//if (_startEntities.Length < _filteredAllEntitiesCount * 2)
|
||||
//{
|
||||
// _startEntities = new int[_filteredAllEntitiesCount * 2];
|
||||
//}
|
||||
//Подготовка массивов
|
||||
if (_sourceEntities.Length < _filteredAllEntitiesCount * 2)
|
||||
{
|
||||
_sourceEntities = new int[_filteredAllEntitiesCount * 2];
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
//установка текущего массива
|
||||
_currentFilteredEntities = _filteredAllEntities;
|
||||
_currentFilteredEntitiesCount = _filteredAllEntitiesCount;
|
||||
@ -106,13 +105,17 @@ namespace DCFApixels.DragonECS
|
||||
{
|
||||
_targetWorldCapacity = World.Capacity;
|
||||
_linkedListSourceHeads = new LinkedListHead[_targetWorldCapacity];
|
||||
_startEntities = new int[_targetWorldCapacity];
|
||||
//_startEntities = new int[_targetWorldCapacity];
|
||||
}
|
||||
else
|
||||
{
|
||||
ArrayUtility.Fill(_linkedListSourceHeads, default); //TODO оптимизировать, сделав не полную отчистку а только по элементов с прошлого раза
|
||||
//ArrayUtility.Fill(_linkedListSourceHeads, default); //TODO оптимизировать, сделав не полную отчистку а только по элементов с прошлого раза
|
||||
for (int i = 0; i < _sourceEntitiesCount; i++)
|
||||
{
|
||||
_linkedListSourceHeads[_sourceEntities[i]] = default;
|
||||
}
|
||||
_startEntitiesCount = 0;
|
||||
}
|
||||
_sourceEntitiesCount = 0;
|
||||
_linkedList.Clear();
|
||||
|
||||
//Заполнение массивов
|
||||
@ -249,7 +252,7 @@ namespace DCFApixels.DragonECS
|
||||
ref var basket = ref _linkedListSourceHeads[sourceEntityID];
|
||||
if (basket.head == 0)
|
||||
{
|
||||
_startEntities[_startEntitiesCount++] = sourceEntityID;
|
||||
_sourceEntities[_sourceEntitiesCount++] = sourceEntityID;
|
||||
basket.head = _linkedList.NewHead(relationEntityID);
|
||||
}
|
||||
else
|
||||
@ -309,7 +312,7 @@ namespace DCFApixels.DragonECS
|
||||
#region GetEntites
|
||||
internal EcsSpan GetSourceEntities()
|
||||
{
|
||||
return UncheckedCoreUtility.CreateSpan(WorldID, _startEntities, _startEntitiesCount);
|
||||
return UncheckedCoreUtility.CreateSpan(WorldID, _sourceEntities, _sourceEntitiesCount);
|
||||
}
|
||||
internal EcsSpan GetRelEntities()
|
||||
{
|
||||
|
@ -1,7 +1,4 @@
|
||||
using System;
|
||||
using System.Collections;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Runtime.CompilerServices;
|
||||
using System.Runtime.InteropServices;
|
||||
|
||||
@ -46,64 +43,18 @@ namespace DCFApixels.DragonECS.Graphs.Internal
|
||||
{
|
||||
return 1 << (GetHighBitNumber((uint)minSize - 1u) + 1);
|
||||
}
|
||||
public static void Fill<T>(T[] array, T value, int startIndex = 0, int length = -1)
|
||||
{
|
||||
if (length < 0)
|
||||
{
|
||||
length = array.Length;
|
||||
}
|
||||
else
|
||||
{
|
||||
length = startIndex + length;
|
||||
}
|
||||
for (int i = startIndex; i < length; i++)
|
||||
{
|
||||
array[i] = value;
|
||||
}
|
||||
}
|
||||
}
|
||||
internal readonly struct EnumerableInt : IEnumerable<int>
|
||||
{
|
||||
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<int> IEnumerable<int>.GetEnumerator() => GetEnumerator();
|
||||
IEnumerator IEnumerable.GetEnumerator() => GetEnumerator();
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public Enumerator GetEnumerator() => new Enumerator(start, start + length);
|
||||
public struct Enumerator : IEnumerator<int>
|
||||
{
|
||||
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
|
||||
{
|
||||
private static class MetaCache<T>
|
||||
{
|
||||
public readonly static int Size;
|
||||
static MetaCache()
|
||||
{
|
||||
T def = default;
|
||||
Size = Marshal.SizeOf(def);
|
||||
}
|
||||
}
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public static T* New<T>(int capacity) where T : unmanaged
|
||||
{
|
||||
@ -112,28 +63,32 @@ namespace DCFApixels.DragonECS.Graphs.Internal
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public static void New<T>(out T* ptr, int capacity) where T : unmanaged
|
||||
{
|
||||
ptr = (T*)Marshal.AllocHGlobal(Marshal.SizeOf<T>(default) * capacity).ToPointer();
|
||||
ptr = (T*)Marshal.AllocHGlobal(Marshal.SizeOf<T>() * capacity).ToPointer();
|
||||
}
|
||||
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public static T* NewAndInit<T>(int capacity) where T : unmanaged
|
||||
{
|
||||
int newSize = Marshal.SizeOf(typeof(T)) * capacity;
|
||||
int newSize = MetaCache<T>.Size * capacity;
|
||||
byte* newPointer = (byte*)Marshal.AllocHGlobal(newSize).ToPointer();
|
||||
|
||||
for (int i = 0; i < newSize; i++)
|
||||
{
|
||||
*(newPointer + i) = 0;
|
||||
}
|
||||
|
||||
return (T*)newPointer;
|
||||
}
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public static void NewAndInit<T>(out T* ptr, int capacity) where T : unmanaged
|
||||
{
|
||||
int newSize = Marshal.SizeOf(typeof(T)) * capacity;
|
||||
int newSize = MetaCache<T>.Size * capacity;
|
||||
byte* newPointer = (byte*)Marshal.AllocHGlobal(newSize).ToPointer();
|
||||
|
||||
for (int i = 0; i < newSize; i++)
|
||||
{
|
||||
*(newPointer + i) = 0;
|
||||
}
|
||||
|
||||
ptr = (T*)newPointer;
|
||||
}
|
||||
@ -167,12 +122,12 @@ namespace DCFApixels.DragonECS.Graphs.Internal
|
||||
{
|
||||
return (T*)Marshal.ReAllocHGlobal(
|
||||
new IntPtr(oldPointer),
|
||||
new IntPtr(Marshal.SizeOf<T>(default) * newCount)).ToPointer();
|
||||
new IntPtr(MetaCache<T>.Size * newCount)).ToPointer();
|
||||
}
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public static T* ResizeAndInit<T>(void* oldPointer, int oldSize, int newSize) where T : unmanaged
|
||||
{
|
||||
int sizeT = Marshal.SizeOf<T>(default);
|
||||
int sizeT = MetaCache<T>.Size;
|
||||
T* result = (T*)Marshal.ReAllocHGlobal(
|
||||
new IntPtr(oldPointer),
|
||||
new IntPtr(sizeT * newSize)).ToPointer();
|
||||
@ -189,16 +144,4 @@ namespace DCFApixels.DragonECS.Graphs.Internal
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public static class CollectionUtility
|
||||
{
|
||||
public static string EntitiesToString(IEnumerable<int> range, string name)
|
||||
{
|
||||
return $"{name}({range.Count()}) {{{string.Join(", ", range.OrderBy(o => o))}}})";
|
||||
}
|
||||
public static string AutoToString<T>(IEnumerable<T> range, string name)
|
||||
{
|
||||
return $"{name}({range.Count()}) {{{string.Join(", ", range.Select(o => o.ToString()))}}})";
|
||||
}
|
||||
}
|
||||
}
|
File diff suppressed because it is too large
Load Diff
@ -1,11 +0,0 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 20978cdfe73c32d49952cb225e32a5b8
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
@ -55,7 +55,17 @@ namespace DCFApixels.DragonECS.Graphs.Internal
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public bool Equals(RelationInfo other) { return this == other; }
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public override int GetHashCode() { return start ^ BitsUtility.NextXorShiftState(end); }
|
||||
public override int GetHashCode()
|
||||
{
|
||||
unchecked
|
||||
{
|
||||
uint endHash = (uint)end;
|
||||
endHash ^= endHash << 13;
|
||||
endHash ^= endHash >> 17;
|
||||
endHash ^= endHash << 5;
|
||||
return start ^ (int)endHash;
|
||||
}
|
||||
}
|
||||
public override string ToString() { return $"arc({start} -> {end})"; }
|
||||
#endregion
|
||||
}
|
||||
|
@ -1,217 +0,0 @@
|
||||
//SparseArray. Analogous to Dictionary<int, T>, but faster.
|
||||
//Benchmark result of indexer.get speed test with 300 elements:
|
||||
//[Dictinary: 5.786us] [SparseArray: 2.047us].
|
||||
using System;
|
||||
using System.Diagnostics.Contracts;
|
||||
using System.Runtime.CompilerServices;
|
||||
using System.Runtime.InteropServices;
|
||||
|
||||
namespace DCFApixels.DragonECS.Graphs.Internal
|
||||
{
|
||||
internal class SparseArray<TValue>
|
||||
{
|
||||
public const int MIN_CAPACITY_BITS_OFFSET = 4;
|
||||
public const int MIN_CAPACITY = 1 << MIN_CAPACITY_BITS_OFFSET;
|
||||
private const int EMPTY = -1;
|
||||
|
||||
private int[] _buckets = Array.Empty<int>();
|
||||
private Entry[] _entries = Array.Empty<Entry>();
|
||||
|
||||
private int _count;
|
||||
|
||||
private int _freeList;
|
||||
private int _freeCount;
|
||||
|
||||
private int _modBitMask;
|
||||
|
||||
#region Properties
|
||||
public TValue this[int keyX, int keyY]
|
||||
{
|
||||
get => _entries[FindEntry((keyX << 32) | keyY)].value;
|
||||
set => Insert(keyX + (keyY << 32), value);
|
||||
}
|
||||
public TValue this[int key]
|
||||
{
|
||||
get => _entries[FindEntry(key)].value;
|
||||
set => Insert(key, value);
|
||||
}
|
||||
public int Count => _count;
|
||||
#endregion
|
||||
|
||||
#region Constructors
|
||||
public SparseArray(int minCapacity = MIN_CAPACITY)
|
||||
{
|
||||
minCapacity = NormalizeCapacity(minCapacity);
|
||||
_buckets = new int[minCapacity];
|
||||
for (int i = 0; i < minCapacity; i++)
|
||||
_buckets[i] = EMPTY;
|
||||
_entries = new Entry[minCapacity];
|
||||
_modBitMask = (minCapacity - 1) & 0x7FFFFFFF;
|
||||
}
|
||||
#endregion
|
||||
|
||||
#region Add/Contains/Remove
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public void Add(int keyX, int keyY, TValue value) => Add((keyX << 32) | keyY, value);
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public void Add(int key, TValue value)
|
||||
{
|
||||
#if DEBUG
|
||||
if (Contains(key))
|
||||
throw new ArgumentException("Contains(hashKey) is true");
|
||||
#endif
|
||||
Insert(key, value);
|
||||
}
|
||||
|
||||
public bool Contains(int keyX, int keyY) => FindEntry((keyX << 32) | keyY) >= 0;
|
||||
public bool Contains(int key) => FindEntry(key) >= 0;
|
||||
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public bool Remove(int keyX, int keyY) => Remove((keyX << 32) | keyY);
|
||||
public bool Remove(int key)
|
||||
{
|
||||
int bucket = key & _modBitMask;
|
||||
int last = -1;
|
||||
for (int i = _buckets[bucket]; i >= 0; last = i, i = _entries[i].next)
|
||||
{
|
||||
if (_entries[i].hashKey == key)
|
||||
{
|
||||
if (last < 0)
|
||||
{
|
||||
_buckets[bucket] = _entries[i].next;
|
||||
}
|
||||
else
|
||||
{
|
||||
_entries[last].next = _entries[i].next;
|
||||
}
|
||||
_entries[i].next = _freeList;
|
||||
_entries[i].hashKey = -1;
|
||||
_entries[i].value = default;
|
||||
_freeList = i;
|
||||
_freeCount++;
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
#endregion
|
||||
|
||||
#region Find/Insert
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
private int FindEntry(int key)
|
||||
{
|
||||
for (int i = _buckets[key & _modBitMask]; i >= 0; i = _entries[i].next)
|
||||
if (_entries[i].hashKey == key) return i;
|
||||
return -1;
|
||||
}
|
||||
private void Insert(int key, TValue value)
|
||||
{
|
||||
int targetBucket = key & _modBitMask;
|
||||
|
||||
for (int i = _buckets[targetBucket]; i >= 0; i = _entries[i].next)
|
||||
{
|
||||
if (_entries[i].hashKey == key)
|
||||
{
|
||||
_entries[i].value = value;
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
int index;
|
||||
if (_freeCount > 0)
|
||||
{
|
||||
index = _freeList;
|
||||
_freeList = _entries[index].next;
|
||||
_freeCount--;
|
||||
}
|
||||
else
|
||||
{
|
||||
if (_count == _entries.Length)
|
||||
{
|
||||
Resize();
|
||||
targetBucket = key & _modBitMask;
|
||||
}
|
||||
index = _count++;
|
||||
}
|
||||
|
||||
_entries[index].next = _buckets[targetBucket];
|
||||
_entries[index].hashKey = key;
|
||||
_entries[index].value = value;
|
||||
_buckets[targetBucket] = index;
|
||||
}
|
||||
#endregion
|
||||
|
||||
#region TryGetValue
|
||||
public bool TryGetValue(int key, out TValue value)
|
||||
{
|
||||
int index = FindEntry(key);
|
||||
if (index < 0)
|
||||
{
|
||||
value = default;
|
||||
return false;
|
||||
}
|
||||
value = _entries[index].value;
|
||||
return true;
|
||||
}
|
||||
#endregion
|
||||
|
||||
#region Clear
|
||||
public void Clear()
|
||||
{
|
||||
if (_count > 0)
|
||||
{
|
||||
for (int i = 0; i < _buckets.Length; i++)
|
||||
{
|
||||
_buckets[i] = -1;
|
||||
}
|
||||
Array.Clear(_entries, 0, _count);
|
||||
_count = 0;
|
||||
}
|
||||
}
|
||||
#endregion
|
||||
|
||||
#region Resize
|
||||
private void Resize()
|
||||
{
|
||||
int newSize = _buckets.Length << 1;
|
||||
_modBitMask = (newSize - 1) & 0x7FFFFFFF;
|
||||
|
||||
Contract.Assert(newSize >= _entries.Length);
|
||||
int[] newBuckets = new int[newSize];
|
||||
for (int i = 0; i < newBuckets.Length; i++)
|
||||
newBuckets[i] = EMPTY;
|
||||
|
||||
Entry[] newEntries = new Entry[newSize];
|
||||
Array.Copy(_entries, 0, newEntries, 0, _count);
|
||||
for (int i = 0; i < _count; i++)
|
||||
{
|
||||
if (newEntries[i].hashKey >= 0)
|
||||
{
|
||||
int bucket = newEntries[i].hashKey % newSize;
|
||||
newEntries[i].next = newBuckets[bucket];
|
||||
newBuckets[bucket] = i;
|
||||
}
|
||||
}
|
||||
_buckets = newBuckets;
|
||||
_entries = newEntries;
|
||||
}
|
||||
|
||||
private int NormalizeCapacity(int capacity)
|
||||
{
|
||||
int result = MIN_CAPACITY;
|
||||
while (result < capacity) result <<= 1;
|
||||
return result;
|
||||
}
|
||||
#endregion
|
||||
|
||||
#region Utils
|
||||
[StructLayout(LayoutKind.Sequential, Pack = 4)]
|
||||
private struct Entry
|
||||
{
|
||||
public int next; // Index of next entry, -1 if last
|
||||
public int hashKey;
|
||||
public TValue value;
|
||||
}
|
||||
#endregion
|
||||
}
|
||||
}
|
@ -1,11 +0,0 @@
|
||||
fileFormatVersion: 2
|
||||
guid: c3dd2571d8641a142a77b4dc93c8a33b
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
Loading…
Reference in New Issue
Block a user