rework EcsGroup

for performance
This commit is contained in:
Mikhail 2023-03-30 10:46:57 +08:00
parent 624abd5b51
commit 14e1a0ae09
2 changed files with 133 additions and 68 deletions

View File

@ -1,83 +1,138 @@
using System; using System;
using System.Runtime.CompilerServices; using System.Runtime.CompilerServices;
using delayedOp = System.Int32;
namespace DCFApixels.DragonECS namespace DCFApixels.DragonECS
{ {
public interface IEcsReadonlyGroup public interface IEcsReadonlyGroup
{ {
public IEcsWorld World { get; } int Count { get; }
public int Count { get; } bool Contains(int entityID);
public EcsGroup.Enumerator GetEnumerator(); EcsGroup.Enumerator GetEnumerator();
} }
public interface IEcsGroup : IEcsReadonlyGroup public interface IEcsGroup : IEcsReadonlyGroup
{ {
public void Add(int entityID); void Add(int entityID);
public void Remove(int entityID); void Remove(int entityID);
} }
public class EcsGroup : IEcsGroup // не может содержать значение 0
{ // _delayedOps это int[] для отложенных операций, хранятся отложенные операции в виде int значения, если старший бит = 0 то это опреация добавленияб если = 1 то это операция вычитания
private IEcsWorld _source;
private SparseSet _entities;
private DelayedOp[] _delayedOps; // this collection can only store numbers greater than 0
public class EcsGroup : IEcsGroup
{
public const int DEALAYED_ADD = 0;
public const int DEALAYED_REMOVE = int.MinValue;
private IEcsWorld _source;
private int[] _dense;
private int[] _sparse;
private int _count;
private delayedOp[] _delayedOps;
private int _delayedOpsCount; private int _delayedOpsCount;
private int _lockCount; private int _lockCount;
#region Properties #region Properties
public IEcsWorld World => _source; public IEcsWorld World => _source;
public int Count => _entities.Count; public int Count
{
[MethodImpl(MethodImplOptions.AggressiveInlining)]
get => _count;
}
#endregion #endregion
#region Constrcutors #region Constrcutors
public EcsGroup(IEcsWorld world, int entitiesCapacity, int delayedOpsCapacity = 32) [MethodImpl(MethodImplOptions.AggressiveInlining)]
public EcsGroup(IEcsWorld source, int denseCapacity = 64, int sparseCapacity = 256, int delayedOpsCapacity = 128)
{ {
_source = world; _source = source;
_entities = new SparseSet(entitiesCapacity, entitiesCapacity); _dense = new int[denseCapacity];
_delayedOps = new DelayedOp[delayedOpsCapacity]; _sparse = new int[sparseCapacity];
_delayedOps = new delayedOp[delayedOpsCapacity];
_lockCount = 0; _lockCount = 0;
} _delayedOpsCount = 0;
#endregion
#region Add/Remove _count = 0;
public void Add(int entityID)
{
if (_lockCount > 0)
AddDelayedOp(entityID, true);
_entities.Add(entityID);
}
public void Remove(int entityID)
{
if (_lockCount > 0)
AddDelayedOp(entityID, false);
_entities.Remove(entityID);
}
private void AddDelayedOp(int entityID, bool isAdd)
{
if (_delayedOpsCount >= _delayedOps.Length)
{
Array.Resize(ref _delayedOps, _delayedOps.Length << 1);
}
ref DelayedOp delayedOd = ref _delayedOps[_delayedOpsCount++];
delayedOd.Entity = entityID;
delayedOd.Added = isAdd;
} }
#endregion #endregion
#region Contains #region Contains
[MethodImpl(MethodImplOptions.AggressiveInlining)] [MethodImpl(MethodImplOptions.AggressiveInlining)]
public bool Contains(int entityID) => _entities.Contains(entityID); public bool Contains(int entityID)
{
return /*entityID > 0 && */ entityID < _sparse.Length && _sparse[entityID] > 0;
}
#endregion #endregion
#region add/remove
public void Add(int entityID)
{
if (_lockCount > 0)
{
AddDelayedOp(entityID, DEALAYED_ADD);
return;
}
if (Contains(entityID))
return;
if(++_count >= _dense.Length)
Array.Resize(ref _dense, _dense.Length << 1);
if (entityID > _sparse.Length)
{
int neadedSpace = _sparse.Length;
while (entityID >= neadedSpace)
neadedSpace <<= 1;
Array.Resize(ref _sparse, neadedSpace);
}
_dense[_count] = entityID;
_sparse[entityID] = _count;
}
public void Remove(int entityID)
{
if (_lockCount > 0)
{
AddDelayedOp(entityID, DEALAYED_REMOVE);
return;
}
if (!Contains(entityID))
return;
_dense[_sparse[entityID]] = _dense[_count];
_sparse[_dense[_count--]] = _sparse[entityID];
_sparse[entityID] = 0;
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
private void AddDelayedOp(int entityID, int isAddBitFlag)
{
if (_delayedOpsCount >= _delayedOps.Length)
{
Array.Resize(ref _delayedOps, _delayedOps.Length << 1);
}
_delayedOps[_delayedOpsCount++] = entityID | isAddBitFlag; // delayedOp = entityID add isAddBitFlag
}
#endregion
//TODO добавить автосоритровку при каждом GetEnumerator
#region AddGroup/RemoveGroup #region AddGroup/RemoveGroup
public void AddGroup(IEcsReadonlyGroup group) public void AddGroup(IEcsReadonlyGroup group)
{ {
foreach (var item in group) foreach (var item in group)
{ {
_entities.TryAdd(item.id); Add(item.id);
} }
} }
@ -85,37 +140,38 @@ namespace DCFApixels.DragonECS
{ {
foreach (var item in group) foreach (var item in group)
{ {
_entities.TryRemove(item.id); Remove(item.id);
} }
} }
#endregion #endregion
#region GetEnumerator #region GetEnumerator
[MethodImpl(MethodImplOptions.AggressiveInlining)]
private void Unlock() private void Unlock()
{ {
#if DEBUG #if DEBUG || !DRAGONECS_NO_SANITIZE_CHECKS
if (_lockCount <= 0) if (_lockCount <= 0)
{ {
throw new Exception($"Invalid lock-unlock balance for {nameof(EcsFilter)}."); throw new Exception($"Invalid lock-unlock balance for {nameof(EcsGroup)}.");
} }
#endif #endif
if (--_lockCount <= 0) if (--_lockCount <= 0)
{ {
for (int i = 0; i < _delayedOpsCount; i++) for (int i = 0; i < _delayedOpsCount; i++)
{ {
ref DelayedOp op = ref _delayedOps[i]; delayedOp op = _delayedOps[i];
if (op.Added) if (op >= 0) //delayedOp.IsAdded
{ {
Add(op.Entity); Add(op & int.MaxValue); //delayedOp.Entity
} }
else else
{ {
Remove(op.Entity); Remove(op & int.MaxValue); //delayedOp.Entity
} }
} }
_delayedOpsCount = 0;
} }
} }
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public Enumerator GetEnumerator() public Enumerator GetEnumerator()
{ {
_lockCount++; _lockCount++;
@ -124,39 +180,48 @@ namespace DCFApixels.DragonECS
#endregion #endregion
#region Utils #region Utils
public ref struct Enumerator public struct Enumerator : IDisposable
{ {
private readonly EcsGroup _source; private readonly EcsGroup _source;
private readonly SparseSet _entities; private int _pointer;
private int _index;
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public Enumerator(EcsGroup group) public Enumerator(EcsGroup group)
{ {
_source = group; _source = group;
_entities = group._entities; _pointer = 0;
_index = -1;
} }
private static EcsProfilerMarker _marker = new EcsProfilerMarker("EcsGroup.Enumerator.Current");
public ent Current public ent Current
{ {
[MethodImpl(MethodImplOptions.AggressiveInlining)] [MethodImpl(MethodImplOptions.AggressiveInlining)]
get { return _source.World.GetEntity(_entities[_index]); } get
{
using (_marker.Auto())
return _source.World.GetEntity(_source._dense[_pointer]);
// return _source._dense[_pointer];
}
} }
[MethodImpl(MethodImplOptions.AggressiveInlining)] [MethodImpl(MethodImplOptions.AggressiveInlining)]
public bool MoveNext() => ++_index < _entities.Count; public bool MoveNext()
{
return ++_pointer <= _source.Count;
}
[MethodImpl(MethodImplOptions.AggressiveInlining)] [MethodImpl(MethodImplOptions.AggressiveInlining)]
public void Dispose() => _source.Unlock(); public void Dispose()
{
_source.Unlock();
}
[MethodImpl(MethodImplOptions.AggressiveInlining)] [MethodImpl(MethodImplOptions.AggressiveInlining)]
public void Reset() => _index = -1; public void Reset()
} {
_pointer = -1;
private struct DelayedOp }
{
public bool Added;
public int Entity;
} }
#endregion #endregion
} }

View File

@ -77,7 +77,7 @@ namespace DCFApixels.DragonECS
#region Constructors #region Constructors
public EcsWorld() public EcsWorld()
{ {
_entityDispenser = new IntDispenser(); _entityDispenser = new IntDispenser(1);
_nullPool = new EcsNullPool(this); _nullPool = new EcsNullPool(this);
_pools = new IEcsPool[512]; _pools = new IEcsPool[512];
FillArray(_pools, _nullPool); FillArray(_pools, _nullPool);