From b5d9d0e5e1ad2ad564722fdeac688094772e5d1d Mon Sep 17 00:00:00 2001 From: Mikhail <99481254+DCFApixels@users.noreply.github.com> Date: Sun, 23 Apr 2023 04:10:54 +0800 Subject: [PATCH] implemented JoinAttachQuery --- src/EcsJoinQuery.cs | 90 ++++++++++++++++---- src/EcsWorld.cs | 4 +- src/Entities/EcsEntity.cs | 22 ++--- src/Pools/EcsAttachPool.cs | 2 + src/Utils/EntityLinkedList.cs | 151 ++++++++++++++++++++++++++++++++++ 5 files changed, 242 insertions(+), 27 deletions(-) create mode 100644 src/Utils/EntityLinkedList.cs diff --git a/src/EcsJoinQuery.cs b/src/EcsJoinQuery.cs index d639dc9..353b98e 100644 --- a/src/EcsJoinQuery.cs +++ b/src/EcsJoinQuery.cs @@ -2,6 +2,7 @@ using System; using System.Collections; using System.Collections.Generic; +using System.Linq; using System.Reflection; using System.Runtime.CompilerServices; using Unity.Profiling; @@ -22,7 +23,16 @@ namespace DCFApixels.DragonECS public EcsAttachPool Attach => _targetPool; - private int[] _mapping = Array.Empty(); + private int _targetWorldCapacity = -1; + private int _targetPoolCapacity = -1; + + private int[] _mapping; + private int[] _counts; + private int[] _entites; + private EntityLinkedList _linkedBasket; + + private bool _isJoinExecuted = false; + public bool IsJoinExecuted => _isJoinExecuted; //private LinkedList @@ -36,30 +46,74 @@ namespace DCFApixels.DragonECS { ExecuteWhere(_targetPool.Entites, groupFilter); } + private ProfilerMarker _execute = new ProfilerMarker("Query.ExecuteJoin"); public sealed override void ExecuteJoin() { - ExecuteWhere(_targetPool.Entites, groupFilter); - if (_isInitTargetWorlds == false) InitTargetWorlds(); + _execute.Begin(); + if (_isInitTargetWorlds == false) + { + InitTargetWorlds(); + if (_isInitTargetWorlds == false) + return; + } - if (source.Capacity != _mapping.Length) - _mapping = new int[World.Capacity]; + //Подготовка массивов + if (_targetWorldCapacity < _targetWorld.Capacity) + { + _mapping = new int[_targetWorldCapacity]; + _counts = new int[_targetWorldCapacity]; + _targetWorldCapacity = _targetWorld.Capacity; + } else - ArrayUtility.Fill(_mapping, 0); + { + ArrayUtility.Fill(_counts, 0); + } + ArrayUtility.Fill(_mapping, -1); + if (_targetPoolCapacity < _targetPool.Capacity) + { + _entites = new int[_targetPoolCapacity]; + _linkedBasket.Resize(_targetPoolCapacity); + _targetPoolCapacity = _targetPool.Capacity; + } + else + { + ArrayUtility.Fill(_entites, 0); + } + _linkedBasket.Clear(); + //Конец подготовки массивов + + ExecuteWhere(); foreach (var e in groupFilter) { - int entityID = e.id; + int attachID = e.id; + EcsEntity attachTarget = _targetPool.Read(attachID).Target; + // if (!attachTarget.IsAlive)//TODO пофиксить IsAlive + //{ + // _targetPool.Del(attachID); + // continue; + //} + int attachTargetID = attachTarget.id; + ref int nodeIndex = ref _mapping[attachTargetID]; + if(nodeIndex< 0) + nodeIndex = _linkedBasket.Add(attachID); + else + _linkedBasket.Insert(nodeIndex, attachID); + _counts[attachTargetID]++; } - } + _isJoinExecuted = true; + _execute.End(); + } + public EntityLinkedList.EnumerableSpan GetNodes(int entityID) => _linkedBasket.Span(_mapping[entityID], _counts[entityID]); private void InitTargetWorlds() { - foreach (var e in groupFilter) + foreach (var e in _targetPool.Entites) { ref readonly var rel = ref _targetPool.Read(e); - if (rel.Target.IsNotNull) + //if (rel.Target.IsNotNull) _targetWorld = EcsWorld.Worlds[rel.Target.world]; if (_targetWorld != null) @@ -68,16 +122,22 @@ namespace DCFApixels.DragonECS break; } } + + if (_isInitTargetWorlds) + { + _targetWorldCapacity = _targetWorld.Capacity; + _mapping = new int[_targetWorldCapacity]; + _counts = new int[_targetWorldCapacity]; + + _targetPoolCapacity = _targetPool.Capacity; + _entites = new int[_targetPoolCapacity]; + _linkedBasket = new EntityLinkedList(_targetPoolCapacity); + } } public EcsGroup.Enumerator GetEnumerator() { return groupFilter.GetEnumerator(); } - - public NodesEnumrable GetNodes(int entityID) - { - throw new NotImplementedException(); - } } public abstract class EcsJoinRelationQuery : EcsJoinQueryBase where TRelationComponent : struct, IEcsRelationComponent diff --git a/src/EcsWorld.cs b/src/EcsWorld.cs index 3b4e8f4..b4fcc4e 100644 --- a/src/EcsWorld.cs +++ b/src/EcsWorld.cs @@ -94,7 +94,7 @@ namespace DCFApixels.DragonECS _groups = new List>(); _allEntites = GetGroupFromPool(); - _queries = new EcsQuery[128]; + _queries = new EcsQueryBase[128]; _entityCreate = _pipeline.GetRunner(); _entityDestry = _pipeline.GetRunner(); @@ -241,7 +241,7 @@ namespace DCFApixels.DragonECS return new EcsEntity(entityID, _gens[entityID], uniqueID); } [MethodImpl(MethodImplOptions.AggressiveInlining)] - public bool EntityIsAlive(int entityID, short gen) + public bool EntityIsAlive(int entityID, short gen) //TODO пофиксить EntityIsAlive { return _gens[entityID] == gen; } diff --git a/src/Entities/EcsEntity.cs b/src/Entities/EcsEntity.cs index 17ea470..611eec5 100644 --- a/src/Entities/EcsEntity.cs +++ b/src/Entities/EcsEntity.cs @@ -25,6 +25,8 @@ namespace DCFApixels.DragonECS //public ent ToEnt() => EcsWorld.Worlds[world].EntityIsAlive(id, gen) ? new ent(id) : default; public ent ToEnt() => new ent(id); + public bool IsAlive => EcsWorld.Worlds[world].EntityIsAlive(id, gen); + #region Constructors [MethodImpl(MethodImplOptions.AggressiveInlining)] public EcsEntity(int id, short gen, short world) : this() @@ -98,15 +100,15 @@ namespace DCFApixels.DragonECS { private static EcsProfilerMarker _IsAliveMarker = new EcsProfilerMarker("EcsEntity.IsAlive"); - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static bool IsAlive(this ref EcsEntity self) - { - //using (_IsAliveMarker.Auto()) - //{ - bool result = EcsWorld.Worlds[self.world].EntityIsAlive(self.id, self.gen); - if (!result) self = EcsEntity.NULL; - return result; - //} - } + // [MethodImpl(MethodImplOptions.AggressiveInlining)] + // public static bool IsAlive(this ref EcsEntity self) + // { + // //using (_IsAliveMarker.Auto()) + // //{ + // bool result = EcsWorld.Worlds[self.world].EntityIsAlive(self.id, self.gen); + // if (!result) self = EcsEntity.NULL; + // return result; + // //} + // } } } diff --git a/src/Pools/EcsAttachPool.cs b/src/Pools/EcsAttachPool.cs index 0a36df1..ec2cb8d 100644 --- a/src/Pools/EcsAttachPool.cs +++ b/src/Pools/EcsAttachPool.cs @@ -33,6 +33,8 @@ namespace DCFApixels.DragonECS _source = world; _poolRunners = new PoolRunners(world.Pipeline); + _entities = EcsGroup.New(world); + _entityFlags = new bool[world.Capacity]; _items = new T[world.Capacity]; _count = 0; diff --git a/src/Utils/EntityLinkedList.cs b/src/Utils/EntityLinkedList.cs new file mode 100644 index 0000000..1e4705a --- /dev/null +++ b/src/Utils/EntityLinkedList.cs @@ -0,0 +1,151 @@ +using System; +using System.Collections; +using System.Collections.Generic; +using System.Linq; +using System.Reflection; +using System.Runtime.InteropServices; +using System.Text; +using System.Threading.Tasks; +using UnityEditor; + +namespace DCFApixels.DragonECS +{ + public class EntityLinkedList + { + public const int First = 0; + + private Node[] _nodes; + private int _count; + private int _lastNodeIndex; + + #region Properties + public int Count => _count; + public int Capacity => _nodes.Length; + public int Last => _lastNodeIndex; + #endregion + + #region Constructors + public EntityLinkedList(int capacity) + { + _nodes = new Node[capacity + 10]; + Clear(); + } + #endregion + + public void Resize(int newCapacity) + { + Array.Resize(ref _nodes, newCapacity + 10); + } + + public void Clear() + { + //ArrayUtility.Fill(_nodes, Node.Empty); + for (int i = 0; i < _nodes.Length; i++) + _nodes[i].next = -1; + _lastNodeIndex = First; + _count = 0; + } + + public void Set(int nodeIndex, int entityID) + { + _nodes[nodeIndex].entityID = entityID; + } + + /// Insert after + /// new node index + public int Insert(int nodeIndex, int entityID) + { + _nodes[_count].Set(entityID, _nodes[nodeIndex].next); + _nodes[nodeIndex].next = _count; + _lastNodeIndex = _count; + return _count++; + } + + public int Add(int entityID) => Insert(_lastNodeIndex, entityID); + + public Enumerator GetEnumerator() => new Enumerator(_nodes); + public EnumerableSpan Span(int startNodeIndex, int count) => new EnumerableSpan(this, startNodeIndex, count); + + #region Utils + [StructLayout(LayoutKind.Sequential, Pack = 4, Size = 8)] + public struct Node + { + public static readonly Node Empty = new Node() { entityID = 0, next = -1 }; + public int entityID; + /// next node index + public int next; + + public void Set(int entityID, int next) + { + this.entityID = entityID; + this.next = next; + } + } + + public struct Enumerator + { + private readonly Node[] _nodes; + private int _index; + private int _next; + + public Enumerator(Node[] nodes) + { + _nodes = nodes; + _index = -1; + _next = First; + } + + public int Current => _nodes[_index].entityID; + + public bool MoveNext() + { + _index = _next; + _next = _nodes[_next].next; + return _index >= 0; + } + } + + public readonly ref struct EnumerableSpan + { + private readonly EntityLinkedList _source; + private readonly int _startNodeIndex; + private readonly int _count; + + public EnumerableSpan(EntityLinkedList source, int startNodeIndex, int count) + { + _source = source; + _startNodeIndex = startNodeIndex; + _count = count; + } + + public SpanEnumerator GetEnumerator() => new SpanEnumerator(_source._nodes, _startNodeIndex, _count); + } + public struct SpanEnumerator + { + private readonly Node[] _nodes; + private int _index; + private int _count; + private int _next; + + public SpanEnumerator(Node[] nodes, int startIndex, int count) + { + _nodes = nodes; + _index = -1; + _count = count; + _next = startIndex; + } + + public int Current => _nodes[_index].entityID; + + public bool MoveNext() + { + if (_count <= 0) + return false; + _index = _next; + _next = _nodes[_next].next; + return _index >= 0 && _count-- > 0; + } + } + #endregion + } +}