This commit is contained in:
Mikhail 2024-03-17 10:18:30 +08:00
parent 74c5f8e8c5
commit df5b50257f
6 changed files with 456 additions and 147 deletions

View File

@ -173,7 +173,7 @@ namespace DCFApixels.DragonECS
return !_relEntityInfos[relEntityID].IsNull; return !_relEntityInfos[relEntityID].IsNull;
} }
[MethodImpl(MethodImplOptions.AggressiveInlining)] [MethodImpl(MethodImplOptions.AggressiveInlining)]
public StartEnd GetRelationInfo(int relEntityID) public StartEnd GetRelationStartEnd(int relEntityID)
{ {
if (relEntityID <= 0 || relEntityID >= _relEntityInfos.Length) if (relEntityID <= 0 || relEntityID >= _relEntityInfos.Length)
{ {
@ -182,14 +182,22 @@ namespace DCFApixels.DragonECS
return new StartEnd(_relEntityInfos[relEntityID]); return new StartEnd(_relEntityInfos[relEntityID]);
} }
[MethodImpl(MethodImplOptions.AggressiveInlining)] [MethodImpl(MethodImplOptions.AggressiveInlining)]
public int GetRelStart(int relEntityID) public int GetRelationStart(int relEntityID)
{ {
return GetRelationInfo(relEntityID).start; if (relEntityID <= 0 || relEntityID >= _relEntityInfos.Length)
{
Throw.UndefinedException();
}
return _relEntityInfos[relEntityID].start;
} }
[MethodImpl(MethodImplOptions.AggressiveInlining)] [MethodImpl(MethodImplOptions.AggressiveInlining)]
public int GetRelEnd(int relEntityID) public int GetRelationEnd(int relEntityID)
{ {
return GetRelationInfo(relEntityID).end; if (relEntityID <= 0 || relEntityID >= _relEntityInfos.Length)
{
Throw.UndefinedException();
}
return _relEntityInfos[relEntityID].end;
} }
#endregion #endregion

View File

@ -80,23 +80,17 @@ namespace DCFApixels.DragonECS
#region Extension #region Extension
public static bool IsRegistered(this EcsArcWorld self) public static bool IsRegistered(this EcsArcWorld self)
{ {
if (self == null) if (self == null) { Throw.ArgumentNull(); }
{
Throw.ArgumentNull();
}
int id = self.id; int id = self.id;
return id < _arcsMapping.Length && _arcsMapping[self.id] != null; return id < _arcsMapping.Length && _arcsMapping[self.id] != null;
} }
public static EcsArc GetRegisteredArc(this EcsArcWorld self) public static EcsArc GetRegisteredArc(this EcsArcWorld self)
{ {
if (self == null) if (self == null) { Throw.ArgumentNull(); }
{
Throw.ArgumentNull();
}
int id = self.id; int id = self.id;
if (id < _arcsMapping.Length && _arcsMapping[self.id] == null) if (id < _arcsMapping.Length && _arcsMapping[self.id] == null)
{ {
throw new Exception(); Throw.UndefinedException();
} }
return _arcsMapping[self.id]; return _arcsMapping[self.id];
} }
@ -105,10 +99,7 @@ namespace DCFApixels.DragonECS
public static EcsArc SetLoopArcAuto<TWorld>(this TWorld self, out EcsLoopArcWorld<TWorld> arcWorld, IConfigContainer config = null) public static EcsArc SetLoopArcAuto<TWorld>(this TWorld self, out EcsLoopArcWorld<TWorld> arcWorld, IConfigContainer config = null)
where TWorld : EcsWorld where TWorld : EcsWorld
{ {
if (self == null) if (self == null) { Throw.ArgumentNull(); }
{
Throw.ArgumentNull();
}
if (typeof(TWorld) != self.GetType()) if (typeof(TWorld) != self.GetType())
{ {
EcsDebug.PrintWarning($"{nameof(TWorld)} is not {self.GetType().Name}"); EcsDebug.PrintWarning($"{nameof(TWorld)} is not {self.GetType().Name}");
@ -120,10 +111,7 @@ namespace DCFApixels.DragonECS
where TStartWorld : EcsWorld where TStartWorld : EcsWorld
where TEndWorld : EcsWorld where TEndWorld : EcsWorld
{ {
if (start == null || end == null) if (start == null || end == null) { Throw.ArgumentNull(); }
{
Throw.ArgumentNull();
}
if (typeof(TStartWorld) == typeof(EcsWorld) && typeof(TEndWorld) == typeof(EcsWorld)) if (typeof(TStartWorld) == typeof(EcsWorld) && typeof(TEndWorld) == typeof(EcsWorld))
{ {
EcsDebug.PrintWarning($"{nameof(TStartWorld)} is not {start.GetType().Name} or {nameof(TEndWorld)} is not {end.GetType().Name}"); EcsDebug.PrintWarning($"{nameof(TStartWorld)} is not {start.GetType().Name} or {nameof(TEndWorld)} is not {end.GetType().Name}");
@ -143,49 +131,52 @@ namespace DCFApixels.DragonECS
return SetArcAuto(start, end, out _, config); return SetArcAuto(start, end, out _, config);
} }
public static EcsArc SetLoopArc(this EcsWorld self, EcsArcWorld arc) => SetArc(self, self, arc); public static EcsArc SetLoopArc(this EcsWorld self, EcsArcWorld arc)
{
return SetArc(self, self, arc);
}
public static EcsArc SetArc(this EcsWorld start, EcsWorld end, EcsArcWorld arc) public static EcsArc SetArc(this EcsWorld start, EcsWorld end, EcsArcWorld arc)
{ {
if (start == null || end == null || arc == null) if (start == null || end == null || arc == null) { Throw.ArgumentNull(); }
{
Throw.ArgumentNull();
}
return Register(start, end, arc); return Register(start, end, arc);
} }
public static bool HasLoopArc(this EcsWorld self) => HasArc(self, self); public static bool HasLoopArc(this EcsWorld self)
{
return HasArc(self, self);
}
public static bool HasArc(this EcsWorld start, EcsWorld end) public static bool HasArc(this EcsWorld start, EcsWorld end)
{ {
if (start == null || end == null) if (start == null || end == null) { Throw.ArgumentNull(); }
{
Throw.ArgumentNull();
}
return Has(start, end); return Has(start, end);
} }
public static EcsArc GetLoopArc(this EcsWorld self) => GetArc(self, self); public static EcsArc GetLoopArc(this EcsWorld self)
{
return GetArc(self, self);
}
public static EcsArc GetArc(this EcsWorld start, EcsWorld end) public static EcsArc GetArc(this EcsWorld start, EcsWorld end)
{ {
if (start == null || end == null) if (start == null || end == null) { Throw.ArgumentNull(); }
{
Throw.ArgumentNull();
}
return Get(start, end); return Get(start, end);
} }
public static bool TryGetLoopArc(this EcsWorld self, out EcsArc arc) => TryGetArc(self, self, out arc); public static bool TryGetLoopArc(this EcsWorld self, out EcsArc arc)
{
return TryGetArc(self, self, out arc);
}
public static bool TryGetArc(this EcsWorld start, EcsWorld end, out EcsArc arc) public static bool TryGetArc(this EcsWorld start, EcsWorld end, out EcsArc arc)
{ {
if (start == null || end == null) if (start == null || end == null) { Throw.ArgumentNull(); }
{
Throw.ArgumentNull();
}
bool result = Has(start, end); bool result = Has(start, end);
arc = result ? Get(start, end) : null; arc = result ? Get(start, end) : null;
return result; return result;
} }
public static void DestroyLoopArc(this EcsWorld self) => DestroyArcWith(self, self); public static void DestroyLoopArc(this EcsWorld self)
{
DestroyArcWith(self, self);
}
public static void DestroyArcWith(this EcsWorld start, EcsWorld end) public static void DestroyArcWith(this EcsWorld start, EcsWorld end)
{ {
if (start == null || end == null) if (start == null || end == null)

View File

@ -1,124 +1,40 @@
/* using System;
using System.Collections.Generic;
using System.Linq;
using System.Runtime.CompilerServices;
using System.Text;
using System.Threading.Tasks;
namespace DCFApixels.DragonECS namespace DCFApixels.DragonECS
{ {
public class EcsJoinExecutor<TAspect> : EcsQueryExecutor, IEcsWorldEventListener public class EcsJoinExecutor : EcsQueryExecutor, IEcsWorldEventListener
where TAspect : EcsAspect
{ {
private TAspect _aspect; private long _lastWorldVersion;
//internal EcsGroup _filteredGroup;
private IdsLinkedList _linkedBasket;
private int[] _mapping;
private int[] _counts;
private long _executeVersion;
private int _targetWorldCapacity = -1;
private EcsProfilerMarker _executeMarker = new EcsProfilerMarker("JoinAttach");
#region Properties #region Properties
public TAspect Aspect => _aspect; public sealed override long Version
internal long ExecuteVersion => _executeVersion; {
[MethodImpl(MethodImplOptions.AggressiveInlining)]
get => _lastWorldVersion;
}
#endregion #endregion
#region OnInitialize/OnDestroy #region Callbacks
protected override void OnInitialize() protected override void OnInitialize()
{ {
_linkedBasket = new IdsLinkedList(128);
World.AddListener(this);
_mapping = new int[World.Capacity];
_counts = new int[World.Capacity];
} }
protected override void OnDestroy() protected override void OnDestroy()
{ {
World.RemoveListener(this);
} }
#endregion public void OnWorldResize(int newSize)
public void Clear()
{
_linkedBasket.Clear();
ArrayUtility.Fill(_mapping, 0, 0, _mapping.Length);
ArrayUtility.Fill(_counts, 0, 0, _counts.Length);
}
public IdsLinkedList.Span GetEntitiesFor(int entity)
{
ref var nodeIndex = ref _mapping[entity];
if (nodeIndex <= 0)
return _linkedBasket.EmptySpan();
else
return _linkedBasket.GetSpan(nodeIndex, _counts[entity]);
}
#region Execute
public EcsJoinAttachResult<TAspect> Execute() => ExecuteFor(World.Entities);
public EcsJoinAttachResult<TAspect> ExecuteFor(EcsReadonlyGroup sourceGroup)
{
_executeMarker.Begin();
var world = _aspect.World;
#if (DEBUG && !DISABLE_DEBUG) || ENABLE_DRAGONECS_ASSERT_CHEKS
if (sourceGroup.IsNull) throw new ArgumentNullException();//TODO составить текст исключения.
#endif
#if (DEBUG && !DISABLE_DEBUG) || ENABLE_DRAGONECS_ASSERT_CHEKS
else
if (World != sourceGroup.World) throw new ArgumentException();//TODO составить текст исключения. это проверка на то что пользователь использует правильный мир
#endif
//Подготовка массивов
if (_targetWorldCapacity < World.Capacity)
{
_targetWorldCapacity = World.Capacity;
_mapping = new int[_targetWorldCapacity];
_counts = new int[_targetWorldCapacity];
}
else
{
ArrayUtility.Fill(_counts, 0);
ArrayUtility.Fill(_mapping, 0);
}
_linkedBasket.Clear();
//Конец подготовки массивов
EcsEdge edge = World.GetEdgeWithSelf();
var iterator = new EcsAspectIterator(_aspect, sourceGroup);
foreach (var arcEntityID in iterator)
{
var rel = edge.GetRelationTargets(arcEntityID);
int sorceEntityID = rel.entity;
//if (!CheckMaskInternal(targetWorldWhereQuery.query.mask, attachTargetID)) continue; //TODO проверить что все работает //исчключить все аттачи, цели которых не входят в targetWorldWhereQuery
ref int nodeIndex = ref _mapping[sorceEntityID];
if (nodeIndex <= 0)
nodeIndex = _linkedBasket.Add(arcEntityID);
else
_linkedBasket.InsertAfter(nodeIndex, arcEntityID);
_counts[sorceEntityID]++;
}
_executeVersion++;
_executeMarker.End();
return new EcsJoinAttachResult<TAspect>(_aspect, this, _executeVersion);
}
#endregion
#region IEcsWorldEventListener
void IEcsWorldEventListener.OnWorldResize(int newSize)
{
Array.Resize(ref _mapping, newSize);
Array.Resize(ref _counts, newSize);
}
void IEcsWorldEventListener.OnReleaseDelEntityBuffer(ReadOnlySpan<int> buffer)
{ {
} }
void IEcsWorldEventListener.OnWorldDestroy() public void OnReleaseDelEntityBuffer(ReadOnlySpan<int> buffer)
{
}
public void OnWorldDestroy()
{ {
} }
#endregion #endregion
} }
} }
*/

View File

@ -0,0 +1,264 @@
using DCFApixels.DragonECS.Graphs.Internal;
using DCFApixels.DragonECS.UncheckedCore;
using System;
using System.Runtime.CompilerServices;
namespace DCFApixels.DragonECS
{
public abstract class EcsJoinToGraphExecutor : EcsQueryExecutor, IEcsWorldEventListener
{
private EcsAspect _aspect;
private EcsArcWorld _arcWorld;
private EntityLinkedList _linkedList;
private Basket[] _baskets;
private int[] _startEntities;
private int _startEntitiesCount;
private long _lastWorldVersion;
private int _targetWorldCapacity = -1;
private EcsProfilerMarker _executeMarker = new EcsProfilerMarker("JoinAttach");
#region Properties
protected abstract EcsAspect AspectRaw { get; }
public sealed override long Version
{
[MethodImpl(MethodImplOptions.AggressiveInlining)]
get { return _lastWorldVersion; }
}
public EcsArcWorld ArcWorld
{
[MethodImpl(MethodImplOptions.AggressiveInlining)]
get { return _arcWorld; }
}
#endregion
#region OnInitialize/OnDestroy
protected override void OnInitialize()
{
_linkedList = new EntityLinkedList(World.Capacity);
_baskets = new Basket[World.Capacity];
World.AddListener(this);
_arcWorld = (EcsArcWorld)World;
}
protected override void OnDestroy()
{
World.RemoveListener(this);
}
#endregion
#region Execute
public EcsGraph Execute()
{
return ExecuteFor(World.Entities);
}
public EcsGraph ExecuteFor(EcsSpan span)
{
_executeMarker.Begin();
#if (DEBUG && !DISABLE_DEBUG) || ENABLE_DRAGONECS_ASSERT_CHEKS
if (span.IsNull) { Throw.ArgumentException(""); }//TODO составить текст исключения.
else if (World != span.World) { Throw.ArgumentException(""); } //TODO составить текст исключения. это проверка на то что пользователь использует правильный мир
#endif
if (_lastWorldVersion != World.Version)
{
//Подготовка массивов
if (_targetWorldCapacity < World.Capacity)
{
_targetWorldCapacity = World.Capacity;
_baskets = new Basket[_targetWorldCapacity];
_startEntities = new int[_targetWorldCapacity];
}
else
{
ArrayUtility.Fill(_baskets, default);
}
_startEntitiesCount = 0;
_linkedList.Clear();
//Конец подготовки массивов
EcsArc arc = _arcWorld.GetRegisteredArc();
var iterator = _aspect.GetIteratorFor(span);
foreach (var relationEntityID in iterator)
{
int startEntityID = arc.GetRelationStart(relationEntityID);
if(startEntityID == 0)
{
continue;
}
_startEntities[_startEntitiesCount++] = startEntityID;
ref var basket = ref _baskets[startEntityID];
if (basket.index <= 0)
{
basket.index = _linkedList.Add(relationEntityID);
}
else
{
_linkedList.InsertAfter(basket.index, relationEntityID);
}
basket.count++;
}
_lastWorldVersion = World.Version;
}
else
{
}
_executeMarker.End();
return new EcsGraph(this, UncheckedCoreUtility.CreateSpan(WorldID, _startEntities, _startEntitiesCount));
}
#endregion
#region Internal result methods
[MethodImpl(MethodImplOptions.AggressiveInlining)]
internal EcsGraphSpan GetNodes_Internal(int startEntityID)
{
Basket basket = _baskets[startEntityID];
return new EcsGraphSpan(_linkedList._nodes, basket.index, basket.count);
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
internal int GetNode_Internal(int startEntityID)
{
Basket basket = _baskets[startEntityID];
return basket.count > 0 ? _linkedList.Get(basket.index) : 0;
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
internal int GetNodesCount_Internal(int startEntityID)
{
return _baskets[startEntityID].count;
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
internal int GetCount_Internal()
{
return _linkedList.Count;
}
#endregion
#region IEcsWorldEventListener
void IEcsWorldEventListener.OnWorldResize(int newSize)
{
Array.Resize(ref _baskets, newSize);
}
void IEcsWorldEventListener.OnReleaseDelEntityBuffer(ReadOnlySpan<int> buffer) { }
void IEcsWorldEventListener.OnWorldDestroy() { }
#endregion
#region Basket
public struct Basket
{
public int index;
public int count;
}
#endregion
}
public sealed class EcsJoinExecutor<TAspect> : EcsJoinToGraphExecutor
where TAspect : EcsAspect
{
private TAspect _aspect;
#region Properties
public TAspect Aspect
{
[MethodImpl(MethodImplOptions.AggressiveInlining)]
get { return _aspect; }
}
protected override EcsAspect AspectRaw
{
get { return _aspect; }
}
#endregion
}
#region EcsJoinedSpan/EcsJoined
public readonly ref struct EcsGraphSpan
{
public static EcsGraphSpan Empty
{
[MethodImpl(MethodImplOptions.AggressiveInlining)]
get { return new EcsGraphSpan(null, 0, 0); }
}
private readonly EntityLinkedList.Node[] _nodes;
private readonly int _startNodeIndex;
private readonly int _count;
public int Count
{
[MethodImpl(MethodImplOptions.AggressiveInlining)]
get { return _count; }
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
internal EcsGraphSpan(EntityLinkedList.Node[] nodes, int startNodeIndex, int count)
{
_nodes = nodes;
_startNodeIndex = startNodeIndex;
_count = count;
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public Enumerator GetEnumerator()
{
return new Enumerator(_nodes, _startNodeIndex, _count);
}
public struct Enumerator
{
private readonly EntityLinkedList.Node[] _nodes;
private int _index;
private int _count;
private int _next;
[MethodImpl(MethodImplOptions.AggressiveInlining)]
internal Enumerator(EntityLinkedList.Node[] nodes, int startIndex, int count)
{
_nodes = nodes;
_index = -1;
_count = count;
_next = startIndex;
}
public int Current
{
[MethodImpl(MethodImplOptions.AggressiveInlining)]
get { return _nodes[_index].entityID; }
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public bool MoveNext()
{
_index = _next;
_next = _nodes[_next].next;
return _index > 0 && _count-- > 0;
}
}
}
public readonly ref struct EcsGraph
{
private readonly EcsJoinToGraphExecutor _executer;
private readonly EcsSpan _startEntities;
public EcsSpan StartEntitiesSpan
{
get { return _startEntities; }
}
internal EcsGraph(EcsJoinToGraphExecutor executer, EcsSpan startEntites)
{
_executer = executer;
_startEntities = startEntites;
}
public EcsGraphSpan GetNodes(int startEntityID)
{
return _executer.GetNodes_Internal(startEntityID);
}
public int GetNode(int startEntityID)
{
return _executer.GetNode_Internal(startEntityID);
}
public int GetNodesCount(int startEntityID)
{
return _executer.GetNodesCount_Internal(startEntityID);
}
}
#endregion
}

View File

@ -0,0 +1,130 @@
using System;
using System.Runtime.CompilerServices;
using System.Runtime.InteropServices;
namespace DCFApixels.DragonECS.Graphs.Internal
{
internal class EntityLinkedList
{
public const int Enter = 0;
internal 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
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public void Resize(int newCapacity)
{
Array.Resize(ref _nodes, newCapacity + 10);
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public void Clear()
{
for (int i = 0; i < _nodes.Length; i++)
{
_nodes[i].next = 0;
}
_lastNodeIndex = Enter;
_count = 0;
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public void Set(int nodeIndex, int entityID)
{
_nodes[nodeIndex].entityID = entityID;
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public int Get(int nodeIndex)
{
return _nodes[nodeIndex].entityID;
}
/// <summary> Insert after</summary>
/// <returns> new node index</returns>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public int InsertAfter(int nodeIndex, int entityID)
{
_nodes[++_count].Set(entityID, _nodes[nodeIndex].next);
_nodes[nodeIndex].next = _count;
_lastNodeIndex = _count;
return _count;
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public int Add(int entityID)
{
return InsertAfter(_lastNodeIndex, entityID);
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public Enumerator GetEnumerator()
{
return new Enumerator(_nodes);
}
//[MethodImpl(MethodImplOptions.AggressiveInlining)]
//public EcsJoinedSpan GetSpan(int startNodeIndex, int count)
//{
// return new EcsJoinedSpan(_nodes, startNodeIndex, count);
//}
//[MethodImpl(MethodImplOptions.AggressiveInlining)]
//public EcsJoinedSpan GetEmptySpan()
//{
// return new EcsJoinedSpan(_nodes, 0, 0);
//}
#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;
/// <summary>next node index</summary>
public int next;
[MethodImpl(MethodImplOptions.AggressiveInlining)]
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;
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public Enumerator(Node[] nodes)
{
_nodes = nodes;
_index = -1;
_next = Enter;
}
public int Current
{
[MethodImpl(MethodImplOptions.AggressiveInlining)]
get { return _nodes[_index].entityID; }
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public bool MoveNext()
{
_index = _next;
_next = _nodes[_next].next;
return _index > 0;
}
}
#endregion
}
}