This commit is contained in:
Mikhail 2026-03-23 15:22:43 +08:00
parent 8e3a2d68c6
commit f1b13c94a7
2 changed files with 144 additions and 91 deletions

View File

@ -43,11 +43,17 @@ namespace DCFApixels.DragonECS
private EcsMaskChunck _maskBit;
private HMem<int> _dense;
private int _denseCount = 0;
private (int ComponentPosIndex, int EntityPosIndex)[] _mapping;// index = entityID / value = itemIndex;/ value = 0 = no entityID.
private T[] _items; // dense; _items[0] - fake component.
private int[] _mapping;// index = entityID / value = itemIndex;/ value = 0 = no entityID.
private (T Cmp, int EntityID)[] _items; // dense; _items[0] - fake component.
private int _itemsCount = 0;
private int _recycledItemsCount;
private int _usedBlockCount;
private struct Slot
{
public T Cmp;
public int EntityID;
}
private readonly IEcsComponentLifecycle<T> _customLifecycle = EcsComponentLifecycle<T>.CustomHandler;
private readonly bool _isCustomLifecycle = EcsComponentLifecycle<T>.IsCustom;
@ -103,7 +109,7 @@ namespace DCFApixels.DragonECS
{
recycledCapacity = capacity / 2;
}
_items = new T[capacity];
_items = new (T, int)[capacity];
//_dense = new int[capacity];
_dense = Alloc<int>(capacity);
}
@ -115,11 +121,11 @@ namespace DCFApixels.DragonECS
_componentTypeID = componentTypeID;
_maskBit = EcsMaskChunck.FromID(componentTypeID);
_mapping = new (int,int)[world.Capacity];
_mapping = new int[world.Capacity];
var worldConfig = world.Configs.GetWorldConfigOrDefault();
if (_items == null)
{
_items = new T[ArrayUtility.CeilPow2Safe(worldConfig.PoolComponentsCapacity)];
_items = new (T, int)[ArrayUtility.CeilPow2Safe(worldConfig.PoolComponentsCapacity)];
//_dense = new int[ArrayUtility.CeilPow2Safe(worldConfig.PoolComponentsCapacity)];
_dense = Alloc<int>(ArrayUtility.CeilPow2Safe(worldConfig.PoolComponentsCapacity));
}
@ -134,7 +140,7 @@ namespace DCFApixels.DragonECS
#if DEBUG
if (entityID == EcsConsts.NULL_ENTITY_ID) { EcsPoolThrowHelper.ThrowEntityIsNotAlive(_world, entityID); }
if (_world.IsUsed(entityID) == false) { EcsPoolThrowHelper.ThrowEntityIsNotAlive(_world, entityID); }
if (mappingSlot.ComponentPosIndex > 0) { EcsPoolThrowHelper.ThrowAlreadyHasComponent<T>(entityID); }
if (mappingSlot > 0) { EcsPoolThrowHelper.ThrowAlreadyHasComponent<T>(entityID); }
if (_isLocked) { EcsPoolThrowHelper.ThrowPoolLocked(); }
#elif DRAGONECS_STABILITY_MODE
if (itemIndex > 0) { return ref Get(entityID); }
@ -142,31 +148,32 @@ namespace DCFApixels.DragonECS
#endif
if (_recycledItemsCount > 0)
{
//mappingSlot.ComponentPosIndex = _recycledItems[--_recycledItemsCount];
//mappingSlot.ComponentPosIndex = _dense[_dense.Length - _recycledItemsCount] & int.MaxValue;
mappingSlot.ComponentPosIndex = _dense.Ptr[_denseCount];
//mappingSlot = _recycledItems[--_recycledItemsCount];
//mappingSlot = _dense[_dense.Length - _recycledItemsCount] & int.MaxValue;
mappingSlot = _dense.Ptr[_itemsCount];
_recycledItemsCount--;
_itemsCount++;
}
else
{
mappingSlot.ComponentPosIndex = ++_itemsCount;
if (mappingSlot.ComponentPosIndex >= _items.Length)
mappingSlot = _itemsCount + 1;
if (mappingSlot >= _items.Length)
{
Array.Resize(ref _items, ArrayUtility.NextPow2(mappingSlot.ComponentPosIndex));
//Array.Resize(ref _dense, ArrayUtility.NextPow2(mappingSlot.ComponentPosIndex));
_dense = Realloc<int>(_dense, ArrayUtility.NextPow2(mappingSlot.ComponentPosIndex));
Array.Resize(ref _items, ArrayUtility.NextPow2(mappingSlot));
//Array.Resize(ref _dense, ArrayUtility.NextPow2(mappingSlot));
_dense = Realloc<int>(_dense, ArrayUtility.NextPow2(mappingSlot));
}
_usedBlockCount++;
}
mappingSlot.EntityPosIndex = _denseCount;
_dense.Ptr[_denseCount++] = entityID;
_dense.Ptr[_itemsCount] = entityID;
_mediator.RegisterComponent(entityID, _componentTypeID, _maskBit);
ref T result = ref _items[mappingSlot.ComponentPosIndex];
EcsComponentLifecycle<T>.OnAdd(_isCustomLifecycle, _customLifecycle, ref result, _worldID, entityID);
ref var slot = ref _items[mappingSlot];
slot.EntityID = entityID;
_itemsCount++;
EcsComponentLifecycle<T>.OnAdd(_isCustomLifecycle, _customLifecycle, ref slot.Cmp, _worldID, entityID);
#if !DRAGONECS_DISABLE_POOLS_EVENTS
if (_hasAnyListener) { _listeners.InvokeOnAddAndGet(entityID); }
#endif
return ref result;
return ref slot.Cmp;
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public ref T Get(int entityID)
@ -177,7 +184,7 @@ namespace DCFApixels.DragonECS
#if !DRAGONECS_DISABLE_POOLS_EVENTS
if (_hasAnyListener) { _listeners.InvokeOnGet(entityID); }
#endif
return ref _items[_mapping[entityID].ComponentPosIndex];
return ref _items[_mapping[entityID]].Cmp;
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public ref readonly T Read(int entityID)
@ -185,89 +192,91 @@ namespace DCFApixels.DragonECS
#if DEBUG // íå íóæåí STAB_MODE
if (!Has(entityID)) { EcsPoolThrowHelper.ThrowNotHaveComponent<T>(entityID); }
#endif
return ref _items[_mapping[entityID].ComponentPosIndex];
return ref _items[_mapping[entityID]].Cmp;
}
public ref T TryAddOrGet(int entityID)
{
#if DEBUG
if (entityID == EcsConsts.NULL_ENTITY_ID) { EcsPoolThrowHelper.ThrowEntityIsNotAlive(_world, entityID); }
#endif
ref var mappingSlot = ref _mapping[entityID];
if (mappingSlot.ComponentPosIndex <= 0)
{ //Add block
#if DEBUG
if (_isLocked) { EcsPoolThrowHelper.ThrowPoolLocked(); }
#elif DRAGONECS_STABILITY_MODE
if (_isLocked) { return ref _items[0]; }
#endif
if (_recycledItemsCount > 0)
if (Has(entityID))
{
//mappingSlot.ComponentPosIndex = _dense[_dense.Length - _recycledItemsCount] & int.MaxValue;
mappingSlot.ComponentPosIndex = _dense.Ptr[_denseCount];
_recycledItemsCount--;
_itemsCount++;
return ref Get(entityID);
}
else
{
mappingSlot.ComponentPosIndex = ++_itemsCount;
if (mappingSlot.ComponentPosIndex >= _items.Length)
{
Array.Resize(ref _items, ArrayUtility.NextPow2(mappingSlot.ComponentPosIndex));
//Array.Resize(ref _dense, ArrayUtility.NextPow2(mappingSlot.ComponentPosIndex));
_dense = Realloc<int>(_dense, ArrayUtility.NextPow2(mappingSlot.ComponentPosIndex));
}
}
mappingSlot.EntityPosIndex = _denseCount;
_dense.Ptr[_denseCount++] = entityID;
_mediator.RegisterComponent(entityID, _componentTypeID, _maskBit);
EcsComponentLifecycle<T>.OnAdd(_isCustomLifecycle, _customLifecycle, ref _items[mappingSlot.ComponentPosIndex], _worldID, entityID);
#if !DRAGONECS_DISABLE_POOLS_EVENTS
if (_hasAnyListener) { _listeners.InvokeOnAdd(entityID); }
#endif
} //Add block end
#if !DRAGONECS_DISABLE_POOLS_EVENTS
if (_hasAnyListener) { _listeners.InvokeOnGet(entityID); }
#endif
return ref _items[mappingSlot.ComponentPosIndex];
return ref Add(entityID);
//#if DEBUG
// if (entityID == EcsConsts.NULL_ENTITY_ID) { EcsPoolThrowHelper.ThrowEntityIsNotAlive(_world, entityID); }
//#endif
// ref var mappingSlot = ref _mapping[entityID];
// if (mappingSlot <= 0)
// { //Add block
//#if DEBUG
// if (_isLocked) { EcsPoolThrowHelper.ThrowPoolLocked(); }
//#elif DRAGONECS_STABILITY_MODE
// if (_isLocked) { return ref _items[0]; }
//#endif
// if (_recycledItemsCount > 0)
// {
// //mappingSlot = _dense[_dense.Length - _recycledItemsCount] & int.MaxValue;
// mappingSlot = _dense.Ptr[_denseCount];
// _recycledItemsCount--;
// _itemsCount++;
// }
// else
// {
// mappingSlot = ++_itemsCount;
// if (mappingSlot >= _items.Length)
// {
// Array.Resize(ref _items, ArrayUtility.NextPow2(mappingSlot));
// //Array.Resize(ref _dense, ArrayUtility.NextPow2(mappingSlot));
// _dense = Realloc<int>(_dense, ArrayUtility.NextPow2(mappingSlot));
// }
// }
// mappingSlot.EntityPosIndex = _denseCount;
// _dense.Ptr[_denseCount++] = entityID;
// _mediator.RegisterComponent(entityID, _componentTypeID, _maskBit);
// EcsComponentLifecycle<T>.OnAdd(_isCustomLifecycle, _customLifecycle, ref _items[mappingSlot], _worldID, entityID);
//#if !DRAGONECS_DISABLE_POOLS_EVENTS
// if (_hasAnyListener) { _listeners.InvokeOnAdd(entityID); }
//#endif
// } //Add block end
//#if !DRAGONECS_DISABLE_POOLS_EVENTS
// if (_hasAnyListener) { _listeners.InvokeOnGet(entityID); }
//#endif
// return ref _items[mappingSlot];
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public bool Has(int entityID)
{
return _mapping[entityID].ComponentPosIndex > 0;
return _mapping[entityID] > 0;
}
public void Del(int entityID)
{
ref var mappingSlot = ref _mapping[entityID];
var mapIndex = _mapping[entityID];
#if DEBUG
if (entityID == EcsConsts.NULL_ENTITY_ID) { EcsPoolThrowHelper.ThrowEntityIsNotAlive(_world, entityID); }
if (mappingSlot.ComponentPosIndex <= 0) { EcsPoolThrowHelper.ThrowNotHaveComponent<T>(entityID); }
if (mappingSlot <= 0) { EcsPoolThrowHelper.ThrowNotHaveComponent<T>(entityID); }
if (_isLocked) { EcsPoolThrowHelper.ThrowPoolLocked(); }
#elif DRAGONECS_STABILITY_MODE
if (itemIndex <= 0) { return; }
if (_isLocked) { return; }
#endif
EcsComponentLifecycle<T>.OnDel(_isCustomLifecycle, _customLifecycle, ref _items[mappingSlot.ComponentPosIndex], _worldID, entityID);
EcsComponentLifecycle<T>.OnDel(_isCustomLifecycle, _customLifecycle, ref _items[mapIndex].Cmp, _worldID, entityID);
_items[mapIndex].EntityID = 0;
//_dense[_sparse[entityID]] = _dense[_count];
//_sparse[_dense[_count--]] = _sparse[entityID];
//_dense[_mapping[entityID].EntityPosIndex] = _dense[_denseCount];
//_mapping[_dense[_denseCount--]].EntityPosIndex = _mapping[entityID].EntityPosIndex;
//_dense[mappingSlot.EntityPosIndex] = _dense[_denseCount];
//_mapping[_dense[_denseCount--]].EntityPosIndex = mappingSlot.EntityPosIndex;
_dense.Ptr[mappingSlot.EntityPosIndex] = _dense.Ptr[--_denseCount];
_mapping[_dense.Ptr[_denseCount]].EntityPosIndex = mappingSlot.EntityPosIndex;
//_dense[_dense.Length - _recycledItemsCount] = mappingSlot.ComponentPosIndex | int.MinValue;
_dense.Ptr[_denseCount] = mappingSlot.ComponentPosIndex;
_recycledItemsCount++;
mappingSlot.ComponentPosIndex = 0;
_itemsCount--;
_dense.Ptr[_itemsCount] = mapIndex;
_mapping[entityID] = 0;
_recycledItemsCount++;
_isDensified = false;
_mediator.UnregisterComponent(entityID, _componentTypeID, _maskBit);
//if(_itemsCount == 0)
//{
// _itemsCount = 0;
// _usedBlockCount = 0;
// _recycledItemsCount = 0;
// _isDensified = true;
//}
#if !DRAGONECS_DISABLE_POOLS_EVENTS
if (_hasAnyListener) { _listeners.InvokeOnDel(entityID); }
#endif
@ -311,16 +320,47 @@ namespace DCFApixels.DragonECS
foreach (var entityID in span)
{
ref var mappingSlot = ref _mapping[entityID];
EcsComponentLifecycle<T>.OnDel(_isCustomLifecycle, _customLifecycle, ref _items[mappingSlot.ComponentPosIndex], _worldID, entityID);
mappingSlot.ComponentPosIndex = 0;
ref var slot = ref _items[mappingSlot];
EcsComponentLifecycle<T>.OnDel(_isCustomLifecycle, _customLifecycle, ref slot.Cmp, _worldID, entityID);
slot.EntityID = 0;
mappingSlot = 0;
_mediator.UnregisterComponent(entityID, _componentTypeID, _maskBit);
#if !DRAGONECS_DISABLE_POOLS_EVENTS
if (_hasAnyListener) { _listeners.InvokeOnDel(entityID); }
#endif
}
_denseCount = 0;
_itemsCount = 0;
_usedBlockCount = 0;
_recycledItemsCount = 0;
_isDensified = true;
}
private bool _isDensified;
private void Densify()
{
if (_isDensified) { return; }
var newUsedBlockCount = 0;
int denseIndex = 0;
for (int i = 0; i < _usedBlockCount; i++)
{
ref var slot = ref _items[i + 1];
if (slot.EntityID != 0)
{
_dense.Ptr[denseIndex] = slot.EntityID;
denseIndex++;
newUsedBlockCount = i + 1;
}
else
{
_dense.Ptr[denseIndex] = i;
}
}
//if (denseIndex != _itemsCount) { Throw.DeepDebugException(); }
_usedBlockCount = newUsedBlockCount;
_recycledItemsCount = newUsedBlockCount - _itemsCount;
_isDensified = true;
}
#endregion
@ -359,7 +399,8 @@ namespace DCFApixels.DragonECS
public EcsSpan ToSpan()
{
return new EcsSpan(_worldID, new ReadOnlySpan<int>(_dense.Ptr, _denseCount));
Densify();
return new EcsSpan(_worldID, new ReadOnlySpan<int>(_dense.Ptr, _itemsCount));
}
#region Listeners

View File

@ -1,6 +1,12 @@
#if DISABLE_DEBUG
#undef DEBUG
#endif
#if !DRAGONECS_DISABLE_POOLS_EVENTS
#define DRAGONECS_ENABLE_POOLS_EVENTS
#else
#undef DRAGONECS_ENABLE_POOLS_EVENTS
#endif
using DCFApixels.DragonECS.Core.Internal;
using DCFApixels.DragonECS.PoolsCore;
using System;
@ -178,7 +184,7 @@ namespace DCFApixels.DragonECS.Core.Internal
void IEcsReadonlyPool.AddListener(IEcsPoolEventListener listener) { }
void IEcsReadonlyPool.RemoveListener(IEcsPoolEventListener listener) { }
#endif
#endregion
#endregion
}
public struct NullComponent { }
}
@ -291,14 +297,15 @@ namespace DCFApixels.DragonECS
/// <summary>Called after deleting an entity from the pool</summary>
void OnDel(int entityID);
}
#if !DRAGONECS_DISABLE_POOLS_EVENTS
public static class PoolEventListExtensions
{
[Conditional("DRAGONECS_ENABLE_POOLS_EVENTS")]
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static void InvokeOnAdd(this List<IEcsPoolEventListener> self, int entityID)
{
for (int i = 0; i < self.Count; i++) { self[i].OnAdd(entityID); }
}
[Conditional("DRAGONECS_ENABLE_POOLS_EVENTS")]
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static void InvokeOnAddAndGet(this List<IEcsPoolEventListener> self, int entityID)
{
@ -308,11 +315,13 @@ namespace DCFApixels.DragonECS
self[i].OnGet(entityID);
}
}
[Conditional("DRAGONECS_ENABLE_POOLS_EVENTS")]
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static void InvokeOnGet(this List<IEcsPoolEventListener> self, int entityID)
{
for (int i = 1; i < self.Count; i++) { self[i].OnGet(entityID); }
}
[Conditional("DRAGONECS_ENABLE_POOLS_EVENTS")]
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static void InvokeOnDel(this List<IEcsPoolEventListener> self, int entityID)
{
@ -322,11 +331,13 @@ namespace DCFApixels.DragonECS
//
[Conditional("DRAGONECS_ENABLE_POOLS_EVENTS")]
[MethodImpl(MethodImplOptions.AggressiveInlining)]
internal static void InvokeOnAdd(this StructList<IEcsPoolEventListener> self, int entityID)
{
for (int i = 0; i < self.Count; i++) { self[i].OnAdd(entityID); }
}
[Conditional("DRAGONECS_ENABLE_POOLS_EVENTS")]
[MethodImpl(MethodImplOptions.AggressiveInlining)]
internal static void InvokeOnAddAndGet(this StructList<IEcsPoolEventListener> self, int entityID)
{
@ -336,17 +347,18 @@ namespace DCFApixels.DragonECS
self[i].OnGet(entityID);
}
}
[Conditional("DRAGONECS_ENABLE_POOLS_EVENTS")]
[MethodImpl(MethodImplOptions.AggressiveInlining)]
internal static void InvokeOnGet(this StructList<IEcsPoolEventListener> self, int entityID)
{
for (int i = 0; i < self.Count; i++) { self[i].OnGet(entityID); }
}
[Conditional("DRAGONECS_ENABLE_POOLS_EVENTS")]
[MethodImpl(MethodImplOptions.AggressiveInlining)]
internal static void InvokeOnDel(this StructList<IEcsPoolEventListener> self, int entityID)
{
for (int i = 0; i < self.Count; i++) { self[i].OnDel(entityID); }
}
}
#endif
#endregion
}