add virtual pool

This commit is contained in:
Mikhail 2024-02-24 03:02:41 +08:00
parent fcc0d1ae97
commit a7276ce966
7 changed files with 432 additions and 11 deletions

View File

@ -143,7 +143,7 @@ namespace DCFApixels.DragonECS
_worldIdDispenser.Release(id);
_isDestroyed = true;
_poolTypeCode_2_CmpTypeIDs = null;
_componentTypeCode_2_CmpTypeIDs = null;
_cmpTypeCode_2_CmpTypeIDs = null;
}
//public void Clear() { }
#endregion

View File

@ -8,7 +8,7 @@ namespace DCFApixels.DragonECS
public partial class EcsWorld
{
private SparseArray<int> _poolTypeCode_2_CmpTypeIDs = new SparseArray<int>();
private SparseArray<int> _componentTypeCode_2_CmpTypeIDs = new SparseArray<int>();
private SparseArray<int> _cmpTypeCode_2_CmpTypeIDs = new SparseArray<int>();
private int _poolsCount;
internal IEcsPoolImplementation[] _pools;
internal int[] _poolComponentCounts;
@ -20,11 +20,20 @@ namespace DCFApixels.DragonECS
#region Getters
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public TPool TestGetPool<TPool>() where TPool : IEcsPoolImplementation, new()
public IEcsPool GetPool(Type componentType)
//TODO. Есть проблема, возврат виртуального пула и последующая девиртуализация сделает ссылку невалидной. Одно из решений возвращать обертку
{
return Get<PoolCache<TPool>>().instance;
#if DEBUG
#endif
int componentTypeID = GetComponentTypeID(componentType);
ref var pool = ref _pools[componentTypeID];
if (pool == _nullPool)
{
pool = new EcsVirtualPool();
pool.OnInit(this, _poolsMediator, componentTypeID);
}
return pool;
}
#if UNITY_2020_3_OR_NEWER
[UnityEngine.Scripting.Preserve]
@ -71,11 +80,11 @@ namespace DCFApixels.DragonECS
}
public bool IsComponentTypeDeclared<TComponent>()
{
return _componentTypeCode_2_CmpTypeIDs.Contains(EcsTypeCode.Get<TComponent>());
return _cmpTypeCode_2_CmpTypeIDs.Contains(EcsTypeCode.Get<TComponent>());
}
public bool IsComponentTypeDeclared(Type componentType)
{
return _componentTypeCode_2_CmpTypeIDs.Contains(EcsTypeCode.Get(componentType));
return _cmpTypeCode_2_CmpTypeIDs.Contains(EcsTypeCode.Get(componentType));
}
public bool IsComponentTypeDeclared(int componentTypeID)
{
@ -94,13 +103,23 @@ namespace DCFApixels.DragonECS
#region Declare/Create
private int DeclareOrGetComponentTypeID(int componentTypeCode)
{
if (!_componentTypeCode_2_CmpTypeIDs.TryGetValue(componentTypeCode, out int ComponentTypeID))
if (_cmpTypeCode_2_CmpTypeIDs.TryGetValue(componentTypeCode, out int ComponentTypeID) == false)
{
ComponentTypeID = _poolsCount++;
_componentTypeCode_2_CmpTypeIDs.Add(componentTypeCode, ComponentTypeID);
_cmpTypeCode_2_CmpTypeIDs.Add(componentTypeCode, ComponentTypeID);
}
return ComponentTypeID;
}
private bool TryDeclareComponentTypeID(int componentTypeCode, out int componentTypeID)
{
if (_cmpTypeCode_2_CmpTypeIDs.TryGetValue(componentTypeCode, out componentTypeID) == false)
{
componentTypeID = _poolsCount++;
_cmpTypeCode_2_CmpTypeIDs.Add(componentTypeCode, componentTypeID);
return true;
}
return false;
}
private TPool CreatePool<TPool>() where TPool : IEcsPoolImplementation, new()
{
int poolTypeCode = EcsTypeCode.Get<TPool>();
@ -123,7 +142,7 @@ namespace DCFApixels.DragonECS
#endif
int componentTypeCode = EcsTypeCode.Get(componentType);
if (_componentTypeCode_2_CmpTypeIDs.TryGetValue(componentTypeCode, out int componentTypeID))
if (_cmpTypeCode_2_CmpTypeIDs.TryGetValue(componentTypeCode, out int componentTypeID))
{
_poolTypeCode_2_CmpTypeIDs[poolTypeCode] = componentTypeID;
}
@ -131,7 +150,7 @@ namespace DCFApixels.DragonECS
{
componentTypeID = _poolsCount++;
_poolTypeCode_2_CmpTypeIDs[poolTypeCode] = componentTypeID;
_componentTypeCode_2_CmpTypeIDs[componentTypeCode] = componentTypeID;
_cmpTypeCode_2_CmpTypeIDs[componentTypeCode] = componentTypeID;
}
if (_poolsCount >= _pools.Length)

View File

@ -210,6 +210,22 @@ namespace DCFApixels.DragonECS
_entities = new int[capacity];
_itemsCount = 0;
}
void IEcsPoolImplementation.OnDevirtualize(EcsVirtualPool.Data data)
{
if (_items.Length < data.ComponentsCount)
{
Array.Resize(ref _items, ArrayUtility.NormalizeSizeToPowerOfTwo(data.ComponentsCount));
}
foreach (var item in data.RawComponents)
{
_mapping[item.EntityID] = ++_itemsCount;
_items[_itemsCount] = (T)item.RawData;
}
foreach (var item in data.Listeners)
{
_listeners.Add(item);
}
}
void IEcsPoolImplementation.OnWorldResize(int newSize)
{
Array.Resize(ref _mapping, newSize);
@ -401,3 +417,54 @@ namespace DCFApixels.DragonECS
}
}
}
public class HybridTypeMapping
{
private EcsWorld _world;
private Dictionary<Type, IEcsHybridPoolInternal> _declared = new Dictionary<Type, IEcsHybridPoolInternal>();
private HashSet<Type> _canInstantiatedTypes = new HashSet<Type>();
public void AddIntsanceType(object instance)
{
_canInstantiatedTypes.Add(instance.GetType());
}
public void Declare<T>()
{
}
private void Init()
{
}
}
//public abstract class HybridBranchBase { }
//public class HybridBranch<T> : HybridBranchBase
// where T : IEcsHybridComponent
//{
// private EcsHybridPool<T> _targetTypePool;
// public HybridBranch(EcsWorld source)
// {
// source.GetHybridPool<T>();
// }
//}
//
//
//
//
//public abstract class HybridNodeBase { }
//public class HybridNode<T> : HybridNodeBase
// where T : IEcsHybridComponent
//{
// private EcsHybridPool<T> _targetTypePool;
// public HybridNode(EcsWorld source)
// {
// source.GetHybridPool<T>();
// }
//}

View File

@ -178,6 +178,22 @@ namespace DCFApixels.DragonECS
_items = new T[ArrayUtility.NormalizeSizeToPowerOfTwo(world.Config.Get_PoolComponentsCapacity())];
_itemsCount = 0;
}
void IEcsPoolImplementation.OnDevirtualize(EcsVirtualPool.Data data)
{
if(_items.Length < data.ComponentsCount)
{
Array.Resize(ref _items, ArrayUtility.NormalizeSizeToPowerOfTwo(data.ComponentsCount));
}
foreach (var item in data.RawComponents)
{
_mapping[item.EntityID] = ++_itemsCount;
_items[_itemsCount] = (T)item.RawData;
}
foreach (var item in data.Listeners)
{
_listeners.Add(item);
}
}
void IEcsPoolImplementation.OnWorldResize(int newSize)
{
Array.Resize(ref _mapping, newSize);

View File

@ -53,6 +53,7 @@ namespace DCFApixels.DragonECS
public interface IEcsPoolImplementation : IEcsPool
{
void OnInit(EcsWorld world, EcsWorld.PoolsMediator mediator, int componentTypeID);
void OnDevirtualize(EcsVirtualPool.Data data);
void OnWorldResize(int newSize);
void OnReleaseDelEntityBuffer(ReadOnlySpan<int> buffer);
void OnWorldDestroy();
@ -113,6 +114,10 @@ namespace DCFApixels.DragonECS
void IEcsPool.SetRaw(int entity, object dataRaw) => throw new NotImplementedException();
void IEcsPool.Copy(int fromEntityID, int toEntityID) => throw new NotImplementedException();
void IEcsPool.Copy(int fromEntityID, EcsWorld toWorld, int toEntityID) => throw new NotImplementedException();
void IEcsPoolImplementation.OnDevirtualize(EcsVirtualPool.Data virtualPoolData)
{
Throw.UndefinedException();
}
#endregion
#region Callbacks

View File

@ -1,3 +1,4 @@
using DCFApixels.DragonECS.Internal;
using System;
using System.Collections;
using System.Collections.Generic;
@ -158,6 +159,18 @@ namespace DCFApixels.DragonECS
_mapping = new bool[world.Capacity];
_count = 0;
}
void IEcsPoolImplementation.OnDevirtualize(EcsVirtualPool.Data data)
{
_count = data.ComponentsCount;
foreach (var item in data.RawComponents)
{
_mapping[item.EntityID] = true;
}
foreach (var item in data.Listeners)
{
_listeners.Add(item);
}
}
void IEcsPoolImplementation.OnWorldResize(int newSize)
{
Array.Resize(ref _mapping, newSize);

301
src/Pools/EcsVirtualPool.cs Normal file
View File

@ -0,0 +1,301 @@
using DCFApixels.DragonECS;
using DCFApixels.DragonECS.Internal;
using System;
using System.Collections;
using System.Collections.Generic;
using System.Runtime.CompilerServices;
public class EcsVirtualPool : IEcsPoolImplementation, IEnumerable
{
private EcsWorld _source;
private Type _componentType;
private int _componentTypeID;
private EcsMaskChunck _maskBit;
private int[] _mapping;
private object[] _items;
private int _itemsCount = 0;
private int[] _recycledItems;
private int _recycledItemsCount;
private List<IEcsPoolEventListener> _listeners = new List<IEcsPoolEventListener>();
private EcsWorld.PoolsMediator _mediator;
#region Properties
public int ComponentID
{
get { return _componentTypeID; }
}
public Type ComponentType
{
get { return _componentType; }
}
public EcsWorld World
{
get { return _source; }
}
public int Count
{
get { return _itemsCount; }
}
public int Capacity
{
get { return _mapping.Length; }
}
#endregion
#region Callbacks
void IEcsPoolImplementation.OnInit(EcsWorld world, EcsWorld.PoolsMediator mediator, int componentTypeID)
{
_componentType = world.GetComponentType(componentTypeID);
_source = world;
_mediator = mediator;
_componentTypeID = componentTypeID;
_maskBit = EcsMaskChunck.FromID(componentTypeID);
_mapping = new int[world.Capacity];
_recycledItems = new int[world.Config.Get_PoolRecycledComponentsCapacity()];
_recycledItemsCount = 0;
_items = new object[ArrayUtility.NormalizeSizeToPowerOfTwo(world.Config.Get_PoolComponentsCapacity())];
_itemsCount = 0;
}
void IEcsPoolImplementation.OnDevirtualize(Data data)
{
Throw.UndefinedException();
}
void IEcsPoolImplementation.OnWorldResize(int newSize)
{
Array.Resize(ref _mapping, newSize);
}
void IEcsPoolImplementation.OnReleaseDelEntityBuffer(ReadOnlySpan<int> buffer)
{
foreach (var entityID in buffer)
{
TryDel(entityID);
}
}
void IEcsPoolImplementation.OnWorldDestroy() { }
#endregion
#region Methods
public void AddRaw(int entityID, object dataRaw)
{
ref int itemIndex = ref _mapping[entityID];
#if (DEBUG && !DISABLE_DEBUG) || ENABLE_DRAGONECS_ASSERT_CHEKS
if (itemIndex > 0) { EcsPoolThrowHalper.ThrowAlreadyHasComponent(_componentType, entityID); }
#endif
if (_recycledItemsCount > 0)
{
itemIndex = _recycledItems[--_recycledItemsCount];
_itemsCount++;
}
else
{
itemIndex = ++_itemsCount;
if (_itemsCount >= _items.Length)
{
Array.Resize(ref _items, _items.Length << 1);
}
}
_items[itemIndex] = dataRaw;
_mediator.RegisterComponent(entityID, _componentTypeID, _maskBit);
_listeners.InvokeOnAddAndGet(entityID);
}
public bool TryAddRaw(int entityID, object dataRaw)
{
if (Has(entityID))
{
return false;
}
AddRaw(entityID, dataRaw);
return true;
}
public void SetRaw(int entityID, object dataRaw)
{
#if (DEBUG && !DISABLE_DEBUG) || ENABLE_DRAGONECS_ASSERT_CHEKS
if (!Has(entityID)) { EcsPoolThrowHalper.ThrowNotHaveComponent(_componentType, entityID); }
#endif
_items[_mapping[entityID]] = dataRaw;
}
public object GetRaw(int entityID)
{
#if (DEBUG && !DISABLE_DEBUG) || ENABLE_DRAGONECS_ASSERT_CHEKS
if (!Has(entityID)) { EcsPoolThrowHalper.ThrowNotHaveComponent(_componentType, entityID); }
#endif
_listeners.InvokeOnGet(entityID);
return _items[_mapping[entityID]];
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public bool Has(int entityID)
{
return _mapping[entityID] > 0;
}
public void Del(int entityID)
{
ref int itemIndex = ref _mapping[entityID];
#if (DEBUG && !DISABLE_DEBUG) || ENABLE_DRAGONECS_ASSERT_CHEKS
if (itemIndex <= 0) EcsPoolThrowHalper.ThrowNotHaveComponent(_componentType, entityID);
#endif
_items[itemIndex] = null;
if (_recycledItemsCount >= _recycledItems.Length)
{
Array.Resize(ref _recycledItems, _recycledItems.Length << 1);
}
_recycledItems[_recycledItemsCount++] = itemIndex;
itemIndex = 0;
_itemsCount--;
_mediator.UnregisterComponent(entityID, _componentTypeID, _maskBit);
_listeners.InvokeOnDel(entityID);
}
public bool TryDel(int entityID)
{
if (Has(entityID))
{
Del(entityID);
return true;
}
return false;
}
public void Copy(int fromEntityID, int toEntityID)
{
throw new NotImplementedException();
}
public void Copy(int fromEntityID, EcsWorld toWorld, int toEntityID)
{
Throw.Exception("Copying data to another world is not supported for virtual pools, devirtualize the pool first.");
}
#endregion
#region Listeners
public void AddListener(IEcsPoolEventListener listener)
{
if (listener == null) { throw new ArgumentNullException("listener is null"); }
_listeners.Add(listener);
}
public void RemoveListener(IEcsPoolEventListener listener)
{
if (listener == null) { throw new ArgumentNullException("listener is null"); }
_listeners.Remove(listener);
}
#endregion
#region IEnumerator - IntelliSense hack
IEnumerator IEnumerable.GetEnumerator() => throw new NotImplementedException();
#endregion
#region Devirtualization
public readonly ref struct Data
{
private readonly EcsVirtualPool _target;
public int ComponentsCount
{
get { return _target.Count; }
}
public RawDataIterator RawComponents
{
get { return new RawDataIterator(_target); }
}
public ListenersIterator Listeners
{
get { return new ListenersIterator(_target); }
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public Data(EcsVirtualPool target)
{
_target = target;
}
public readonly ref struct ListenersIterator
{
private readonly EcsVirtualPool _target;
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public ListenersIterator(EcsVirtualPool target)
{
_target = target;
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public List<IEcsPoolEventListener>.Enumerator GetEnumerator() { return _target._listeners.GetEnumerator(); }
}
public readonly ref struct RawDataIterator
{
private readonly EcsVirtualPool _target;
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public RawDataIterator(EcsVirtualPool target)
{
_target = target;
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public Enumerator GetEnumerator() { return new Enumerator(this); }
public ref struct Enumerator
{
private readonly int[] _mapping;
private readonly object[] _items;
private readonly int _entitesCount;
private int _entityID;
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public Enumerator(RawDataIterator devirtualizator)
{
_mapping = devirtualizator._target._mapping;
_items = devirtualizator._target._items;
_entitesCount = devirtualizator._target.World.Count + 1;
if (_entitesCount > _mapping.Length)
{
_entitesCount = _mapping.Length;
}
_entityID = 0;
}
public EntityRawDataPair Current
{
[MethodImpl(MethodImplOptions.AggressiveInlining)]
get { return new EntityRawDataPair(_entityID, _items[_entityID]); }
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public bool MoveNext()
{
while (_entityID++ < _entitesCount)
{
if (_mapping[_entityID] != 0)
{
return true;
}
}
return false;
}
}
}
}
public readonly struct EntityRawDataPair
{
public readonly int EntityID;
public readonly object RawData;
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public EntityRawDataPair(int entityID, object rawData)
{
EntityID = entityID;
RawData = rawData;
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public void Deconstruct(out int entityID, out object rawData)
{
entityID = EntityID;
rawData = RawData;
}
}
#endregion
}
public static class VirtualPoolExtensions
{
public static bool IsVirtual(this IEcsPool self)
{
return self is EcsVirtualPool;
}
}