This commit is contained in:
Mikhail 2026-03-28 21:46:25 +08:00
parent 9cdae69d17
commit 418df03c84
2 changed files with 128 additions and 27 deletions

View File

@ -3,6 +3,7 @@
#endif #endif
using DCFApixels.DragonECS.Core; using DCFApixels.DragonECS.Core;
using DCFApixels.DragonECS.Core.Internal; using DCFApixels.DragonECS.Core.Internal;
using DCFApixels.DragonECS.Core.Unchecked;
using DCFApixels.DragonECS.PoolsCore; using DCFApixels.DragonECS.PoolsCore;
using System; using System;
using System.Collections; using System.Collections;
@ -49,12 +50,6 @@ namespace DCFApixels.DragonECS
private int _recycledItemsCount; private int _recycledItemsCount;
private int _usedBlockCount; private int _usedBlockCount;
private struct Slot
{
public T Cmp;
public int EntityID;
}
private readonly IEcsComponentLifecycle<T> _customLifecycle = EcsComponentLifecycle<T>.CustomHandler; private readonly IEcsComponentLifecycle<T> _customLifecycle = EcsComponentLifecycle<T>.CustomHandler;
private readonly bool _isCustomLifecycle = EcsComponentLifecycle<T>.IsCustom; private readonly bool _isCustomLifecycle = EcsComponentLifecycle<T>.IsCustom;
private readonly IEcsComponentCopy<T> _customCopy = EcsComponentCopy<T>.CustomHandler; private readonly IEcsComponentCopy<T> _customCopy = EcsComponentCopy<T>.CustomHandler;
@ -101,7 +96,7 @@ namespace DCFApixels.DragonECS
#endregion #endregion
#region Constructors/Init/Destroy #region Constructors/Init/Destroy
public EcsPool() { } public EcsPool() { _isDensified = true; }
public EcsPool(int capacity, int recycledCapacity = -1) public EcsPool(int capacity, int recycledCapacity = -1)
{ {
capacity = ArrayUtility.CeilPow2Safe(capacity); capacity = ArrayUtility.CeilPow2Safe(capacity);
@ -137,6 +132,7 @@ namespace DCFApixels.DragonECS
public ref T Add(int entityID) public ref T Add(int entityID)
{ {
ref var itemIndex = ref _mapping[entityID]; ref var itemIndex = ref _mapping[entityID];
var prevItemIndex = itemIndex;
#if DEBUG #if DEBUG
if (entityID == EcsConsts.NULL_ENTITY_ID) { EcsPoolThrowHelper.ThrowEntityIsNotAlive(_world, entityID); } if (entityID == EcsConsts.NULL_ENTITY_ID) { EcsPoolThrowHelper.ThrowEntityIsNotAlive(_world, entityID); }
if (_world.IsUsed(entityID) == false) { EcsPoolThrowHelper.ThrowEntityIsNotAlive(_world, entityID); } if (_world.IsUsed(entityID) == false) { EcsPoolThrowHelper.ThrowEntityIsNotAlive(_world, entityID); }
@ -150,7 +146,7 @@ namespace DCFApixels.DragonECS
{ {
//mappingSlot = _recycledItems[--_recycledItemsCount]; //mappingSlot = _recycledItems[--_recycledItemsCount];
//mappingSlot = _dense[_dense.Length - _recycledItemsCount] & int.MaxValue; //mappingSlot = _dense[_dense.Length - _recycledItemsCount] & int.MaxValue;
itemIndex = _dense.Ptr[_itemsCount]; itemIndex = _dense.Ptr[_itemsCount + 1];
_recycledItemsCount--; _recycledItemsCount--;
} }
else else
@ -164,12 +160,12 @@ namespace DCFApixels.DragonECS
} }
_usedBlockCount++; _usedBlockCount++;
} }
_dense.Ptr[_itemsCount] = entityID; _dense.Ptr[_itemsCount + 1] = entityID;
_mediator.RegisterComponent(entityID, _componentTypeID, _maskBit); _mediator.RegisterComponent(entityID, _componentTypeID, _maskBit);
ref var slot = ref _items[itemIndex]; ref var slot = ref _items[itemIndex];
slot.EntityID = entityID; slot.EntityID = entityID;
_itemsCount++; _itemsCount++;
EcsComponentLifecycle<T>.OnAdd(_isCustomLifecycle, _customLifecycle, ref slot.Cmp, _worldID, entityID); InvokeOnAdd(entityID, ref _items[itemIndex].Cmp);
#if !DRAGONECS_DISABLE_POOLS_EVENTS #if !DRAGONECS_DISABLE_POOLS_EVENTS
if (_hasAnyListener) { _listeners.InvokeOnAddAndGet(entityID); } if (_hasAnyListener) { _listeners.InvokeOnAddAndGet(entityID); }
#endif #endif
@ -258,11 +254,11 @@ namespace DCFApixels.DragonECS
if (itemIndex <= 0) { return; } if (itemIndex <= 0) { return; }
if (_isLocked) { return; } if (_isLocked) { return; }
#endif #endif
EcsComponentLifecycle<T>.OnDel(_isCustomLifecycle, _customLifecycle, ref _items[itemIndex].Cmp, _worldID, entityID); InvokeOnDel(entityID, ref _items[itemIndex].Cmp);
_items[itemIndex].EntityID = 0; _items[itemIndex].EntityID = 0;
_itemsCount--;
_dense.Ptr[_itemsCount] = itemIndex; _dense.Ptr[_itemsCount] = itemIndex;
_itemsCount--;
_mapping[entityID] = 0; _mapping[entityID] = 0;
_recycledItemsCount++; _recycledItemsCount++;
@ -317,13 +313,19 @@ namespace DCFApixels.DragonECS
_recycledItemsCount = 0; // ñïåðåäè ÷òîáû îáíóëÿëîñü, òàê êàê Del íå îáíóëÿåò _recycledItemsCount = 0; // ñïåðåäè ÷òîáû îáíóëÿëîñü, òàê êàê Del íå îáíóëÿåò
if (_itemsCount <= 0) { return; } if (_itemsCount <= 0) { return; }
var span = _world.Where(out SingleAspect<T> _); var span = _world.Where(out SingleAspect<T> _);
#if DRAGONECS_DEEP_DEBUG
if(span.Count != _itemsCount)
{
Throw.DeepDebugException();
}
#endif
foreach (var entityID in span) foreach (var entityID in span)
{ {
ref var mappingSlot = ref _mapping[entityID]; ref var itemIndex = ref _mapping[entityID];
ref var slot = ref _items[mappingSlot]; ref var slot = ref _items[itemIndex];
EcsComponentLifecycle<T>.OnDel(_isCustomLifecycle, _customLifecycle, ref slot.Cmp, _worldID, entityID); InvokeOnDel(entityID, ref slot.Cmp);
slot.EntityID = 0; slot.EntityID = 0;
mappingSlot = 0; itemIndex = 0;
_mediator.UnregisterComponent(entityID, _componentTypeID, _maskBit); _mediator.UnregisterComponent(entityID, _componentTypeID, _maskBit);
#if !DRAGONECS_DISABLE_POOLS_EVENTS #if !DRAGONECS_DISABLE_POOLS_EVENTS
if (_hasAnyListener) { _listeners.InvokeOnDel(entityID); } if (_hasAnyListener) { _listeners.InvokeOnDel(entityID); }
@ -335,28 +337,78 @@ namespace DCFApixels.DragonECS
_isDensified = true; _isDensified = true;
} }
private bool _isDensified; private bool _isDensified;
#if DRAGONECS_DEEP_DEBUG
private int _invokeDensifyCounter = 0;
private int _lastDensifyAfterIncrement = 0;
#endif
private void Densify() private void Densify()
{ {
if (_isDensified) { return; } //if (_isDensified) { return; }
var newUsedBlockCount = 0; var newUsedBlockCount = 0;
int denseIndex = 0; _dense.Ptr[0] = 0;
for (int i = 0; i < _usedBlockCount; i++) int denseIndex = 1;
int recycleIndex = denseIndex + _itemsCount;
for (int i = 1; i <= _usedBlockCount; i++)
{ {
ref var slot = ref _items[i + 1]; ref var slot = ref _items[i];
if (slot.EntityID != 0) if (slot.EntityID != 0)
{ {
_dense.Ptr[denseIndex] = slot.EntityID; _dense.Ptr[denseIndex++] = slot.EntityID;
denseIndex++; newUsedBlockCount = i;
newUsedBlockCount = i + 1;
} }
else else
{ {
_dense.Ptr[denseIndex] = i; _dense.Ptr[recycleIndex++] = i;
} }
} }
//if (denseIndex != _itemsCount) { Throw.DeepDebugException(); }
#if DRAGONECS_DEEP_DEBUG
HashSet<int> useds = new HashSet<int>();
HashSet<int> recycleds = new HashSet<int>();
for (int i = 1; i <= newUsedBlockCount; i++)
{
var value = _dense.Ptr[i];
if (i <= _itemsCount)
{
if (useds.Add(value) == false)
{
Throw.DeepDebugException();
}
}
else
{
if (recycleds.Add(value) == false)
{
Throw.DeepDebugException();
}
var e = _items[value].EntityID;
bool isHasComponent = Has(e);
bool isUsedsContains = useds.Contains(e);
bool isWorldUsed = _world.IsUsed(e);
if (e != 0 && (isHasComponent || isUsedsContains))
{
Throw.DeepDebugException();
}
}
}
if(useds.Count != _itemsCount)
{
Throw.DeepDebugException();
}
var result = new EcsSpan(_worldID, new ReadOnlySpan<int>(_dense.Ptr + 1, _itemsCount));
UncheckedUtility.CheckSpanValideDebugWithException(result);
_lastDensifyAfterIncrement = 0;
_invokeDensifyCounter++;
if(newUsedBlockCount > _usedBlockCount)
{
Throw.DeepDebugException();
}
#endif
_usedBlockCount = newUsedBlockCount; _usedBlockCount = newUsedBlockCount;
_recycledItemsCount = newUsedBlockCount - _itemsCount; _recycledItemsCount = newUsedBlockCount - _itemsCount;
@ -388,6 +440,9 @@ namespace DCFApixels.DragonECS
[MethodImpl(MethodImplOptions.AggressiveInlining)] [MethodImpl(MethodImplOptions.AggressiveInlining)]
public void InvokeOnAdd(int entityID, ref T component) public void InvokeOnAdd(int entityID, ref T component)
{ {
#if DRAGONECS_DEEP_DEBUG
_lastDensifyAfterIncrement++;
#endif
if (_isCustomLifecycle) if (_isCustomLifecycle)
{ {
_customLifecycle.OnAdd(ref component, _worldID, entityID); _customLifecycle.OnAdd(ref component, _worldID, entityID);
@ -396,6 +451,9 @@ namespace DCFApixels.DragonECS
[MethodImpl(MethodImplOptions.AggressiveInlining)] [MethodImpl(MethodImplOptions.AggressiveInlining)]
public void InvokeOnDel(int entityID, ref T component) public void InvokeOnDel(int entityID, ref T component)
{ {
#if DRAGONECS_DEEP_DEBUG
_lastDensifyAfterIncrement++;
#endif
if (_isCustomLifecycle) if (_isCustomLifecycle)
{ {
_customLifecycle.OnDel(ref component, _worldID, entityID); _customLifecycle.OnDel(ref component, _worldID, entityID);
@ -417,10 +475,42 @@ namespace DCFApixels.DragonECS
} }
#endregion #endregion
private int _toSpans = 0;
public EcsSpan ToSpan() public EcsSpan ToSpan()
{ {
if(_toSpans > 0)
{
return _world.Entities;
}
_toSpans++;
Densify(); Densify();
return new EcsSpan(_worldID, new ReadOnlySpan<int>(_dense.Ptr, _itemsCount)); var result = new EcsSpan(_worldID, new ReadOnlySpan<int>(_dense.Ptr + 1, _itemsCount));
#if DRAGONECS_DEEP_DEBUG
var r2 = _world.WhereToGroup(out SingleAspect<T> _);
if(r2.SetEquals(result) == false)
{
Throw.DeepDebugException();
}
if (result.Count != _itemsCount)
{
Throw.DeepDebugException();
}
foreach (var e in result)
{
if(Has(e) == false)
{
Throw.DeepDebugException();
}
if (_world.IsUsed(e) == false)
{
Throw.DeepDebugException();
}
}
UncheckedUtility.CheckSpanValideDebugWithException(result);
#endif
_toSpans--;
return result;
} }
#region Listeners #region Listeners

View File

@ -60,6 +60,17 @@ namespace DCFApixels.DragonECS.Core.Unchecked
} }
return true; return true;
} }
public static void CheckSpanValideDebugWithException(EcsSpan span)
{
HashSet<int> set = new HashSet<int>();
foreach (var e in span)
{
if (set.Add(e) == false)
{
throw new Exception($"Duplication {e}");
}
}
}
#endregion #endregion
#region Unsafe Span #region Unsafe Span