From a7276ce9666d21153638e9202231e5a8b211a253 Mon Sep 17 00:00:00 2001 From: Mikhail <99481254+DCFApixels@users.noreply.github.com> Date: Sat, 24 Feb 2024 03:02:41 +0800 Subject: [PATCH] add virtual pool --- src/EcsWorld.cs | 2 +- src/EcsWorld.pools.cs | 39 +++-- src/Pools/EcsHybridPool.cs | 67 ++++++++ src/Pools/EcsPool.cs | 16 ++ src/Pools/EcsPoolBase.cs | 5 + src/Pools/EcsTagPool.cs | 13 ++ src/Pools/EcsVirtualPool.cs | 301 ++++++++++++++++++++++++++++++++++++ 7 files changed, 432 insertions(+), 11 deletions(-) create mode 100644 src/Pools/EcsVirtualPool.cs diff --git a/src/EcsWorld.cs b/src/EcsWorld.cs index 5b3ba23..1082a44 100644 --- a/src/EcsWorld.cs +++ b/src/EcsWorld.cs @@ -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 diff --git a/src/EcsWorld.pools.cs b/src/EcsWorld.pools.cs index 4ea01f0..2c8b49b 100644 --- a/src/EcsWorld.pools.cs +++ b/src/EcsWorld.pools.cs @@ -8,7 +8,7 @@ namespace DCFApixels.DragonECS public partial class EcsWorld { private SparseArray _poolTypeCode_2_CmpTypeIDs = new SparseArray(); - private SparseArray _componentTypeCode_2_CmpTypeIDs = new SparseArray(); + private SparseArray _cmpTypeCode_2_CmpTypeIDs = new SparseArray(); private int _poolsCount; internal IEcsPoolImplementation[] _pools; internal int[] _poolComponentCounts; @@ -20,12 +20,21 @@ namespace DCFApixels.DragonECS #region Getters [MethodImpl(MethodImplOptions.AggressiveInlining)] - public TPool TestGetPool() where TPool : IEcsPoolImplementation, new() + public IEcsPool GetPool(Type componentType) + //TODO. Есть проблема, возврат виртуального пула и последующая девиртуализация сделает ссылку невалидной. Одно из решений возвращать обертку { - return Get>().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] #endif @@ -71,11 +80,11 @@ namespace DCFApixels.DragonECS } public bool IsComponentTypeDeclared() { - return _componentTypeCode_2_CmpTypeIDs.Contains(EcsTypeCode.Get()); + return _cmpTypeCode_2_CmpTypeIDs.Contains(EcsTypeCode.Get()); } 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() where TPool : IEcsPoolImplementation, new() { int poolTypeCode = EcsTypeCode.Get(); @@ -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) diff --git a/src/Pools/EcsHybridPool.cs b/src/Pools/EcsHybridPool.cs index 693b344..838ea92 100644 --- a/src/Pools/EcsHybridPool.cs +++ b/src/Pools/EcsHybridPool.cs @@ -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 _declared = new Dictionary(); + + private HashSet _canInstantiatedTypes = new HashSet(); + + public void AddIntsanceType(object instance) + { + _canInstantiatedTypes.Add(instance.GetType()); + } + + public void Declare() + { + + } + + + private void Init() + { + + } +} + + +//public abstract class HybridBranchBase { } +//public class HybridBranch : HybridBranchBase +// where T : IEcsHybridComponent +//{ +// private EcsHybridPool _targetTypePool; +// public HybridBranch(EcsWorld source) +// { +// source.GetHybridPool(); +// } +//} +// +// +// +// +//public abstract class HybridNodeBase { } +//public class HybridNode : HybridNodeBase +// where T : IEcsHybridComponent +//{ +// private EcsHybridPool _targetTypePool; +// public HybridNode(EcsWorld source) +// { +// source.GetHybridPool(); +// } +//} diff --git a/src/Pools/EcsPool.cs b/src/Pools/EcsPool.cs index e8f019c..4bdeeed 100644 --- a/src/Pools/EcsPool.cs +++ b/src/Pools/EcsPool.cs @@ -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); diff --git a/src/Pools/EcsPoolBase.cs b/src/Pools/EcsPoolBase.cs index 97ca510..c3e14fd 100644 --- a/src/Pools/EcsPoolBase.cs +++ b/src/Pools/EcsPoolBase.cs @@ -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 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 diff --git a/src/Pools/EcsTagPool.cs b/src/Pools/EcsTagPool.cs index 1a84e92..d472cb3 100644 --- a/src/Pools/EcsTagPool.cs +++ b/src/Pools/EcsTagPool.cs @@ -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); diff --git a/src/Pools/EcsVirtualPool.cs b/src/Pools/EcsVirtualPool.cs new file mode 100644 index 0000000..45e57cc --- /dev/null +++ b/src/Pools/EcsVirtualPool.cs @@ -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 _listeners = new List(); + + 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 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.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; + } +} \ No newline at end of file