mirror of
https://github.com/DCFApixels/DragonECS.git
synced 2025-09-19 02:24:37 +08:00
add virtual pool
This commit is contained in:
parent
fcc0d1ae97
commit
a7276ce966
@ -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
|
||||
|
@ -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)
|
||||
|
@ -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>();
|
||||
// }
|
||||
//}
|
||||
|
@ -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);
|
||||
|
@ -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
|
||||
|
@ -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
301
src/Pools/EcsVirtualPool.cs
Normal 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;
|
||||
}
|
||||
}
|
Loading…
Reference in New Issue
Block a user