From 7da652e78cab5411012b057f1c2dff786cfbbe5e Mon Sep 17 00:00:00 2001 From: DCFApixels <99481254+DCFApixels@users.noreply.github.com> Date: Sun, 20 Jul 2025 16:06:30 +0800 Subject: [PATCH] stash --- src/Internal/Allocators/MemoryAllocator.cs | 60 +++ src/Pools/EcsValuePool.cs | 537 +++++++++++++++++++++ 2 files changed, 597 insertions(+) create mode 100644 src/Pools/EcsValuePool.cs diff --git a/src/Internal/Allocators/MemoryAllocator.cs b/src/Internal/Allocators/MemoryAllocator.cs index 47e90a6..d833d90 100644 --- a/src/Internal/Allocators/MemoryAllocator.cs +++ b/src/Internal/Allocators/MemoryAllocator.cs @@ -2,6 +2,7 @@ #undef DEBUG #endif using System; +using System.Collections.Generic; using System.Runtime.InteropServices; namespace DCFApixels.DragonECS.Core.Internal @@ -110,6 +111,21 @@ namespace DCFApixels.DragonECS.Core.Internal #endregion #region Realloc + public static Handler ReallocOrAlloc(void* target, int newCount) where T : unmanaged + { + if (target == null) { return Alloc(newCount); } + return Realloc(Handler.FromDataPtr(target), Marshal.SizeOf() * newCount); + } + public static Handler ReallocOrAlloc(void* target, int newByteLength) + { + if (target == null) { return Alloc(newByteLength); } + return Realloc(Handler.FromDataPtr(target), newByteLength); + } + public static Handler ReallocOrAlloc(Handler target, int newCount) where T : unmanaged + { + if (target.IsEmpty) { return Alloc(newCount); } + return Realloc_Internal(target, Marshal.SizeOf() * newCount, typeof(T)); + } public static Handler Realloc(void* target, int newCount) where T : unmanaged { return Realloc(Handler.FromDataPtr(target), Marshal.SizeOf() * newCount); @@ -335,6 +351,50 @@ namespace DCFApixels.DragonECS.Core.Internal } } + internal ref struct AllocBuilder + { + //[ThreadStatic] + //private static Stack _buildersPool; + //private AllocBuilder() { } + //public static AllocBuilder New() + //{ + // if (_buildersPool == null) { _buildersPool = new Stack(4); } + // if(_buildersPool.TryPop(out var result) == false) + // { + // result = new AllocBuilder(); + // } + // return result; + //} + + public static AllocBuilder New() + { + return new AllocBuilder(0); + } + + private int _byteLength; + private AllocBuilder(int byteLength) + { + _byteLength = byteLength; + } + public void Add(int count) where T : unmanaged + { + Add(Marshal.SizeOf()); + } + public void Add(int byteLength) + { + _byteLength += byteLength; + } + + public MemoryAllocator.Handler Alloc() + { + return MemoryAllocator.Alloc(_byteLength); + } + public MemoryAllocator.Handler Realloc(MemoryAllocator.Handler handler) + { + return MemoryAllocator.Realloc(handler, _byteLength); + } + } + internal static class MemoryAllocatorHandlerExtensions { public static void Dispose(this ref MemoryAllocator.Handler self) diff --git a/src/Pools/EcsValuePool.cs b/src/Pools/EcsValuePool.cs new file mode 100644 index 0000000..675e95c --- /dev/null +++ b/src/Pools/EcsValuePool.cs @@ -0,0 +1,537 @@ +#if DISABLE_DEBUG +#undef DEBUG +#endif +using DCFApixels.DragonECS.Core; +using DCFApixels.DragonECS.Core.Internal; +using DCFApixels.DragonECS.PoolsCore; +using System; +using System.Collections; +using System.Collections.Generic; +using System.ComponentModel; +using System.Diagnostics; +using System.Runtime.CompilerServices; +#if ENABLE_IL2CPP +using Unity.IL2CPP.CompilerServices; +#endif + +namespace DCFApixels.DragonECS +{ + /// Value type component + [MetaColor(MetaColor.DragonRose)] + [MetaGroup(EcsConsts.PACK_GROUP, EcsConsts.POOLS_GROUP)] + [MetaDescription(EcsConsts.AUTHOR, "Standard component.")] + [MetaID("DragonECS_84D2537C9201D6F6B92FEC1C8883A07A")] + public interface IEcsValueComponent : IEcsComponentMember { } + + internal unsafe struct EcsValuePoolSharedStore + { + public int _componentTypeID; + public int* _mapping; + public void* _items; + } + internal readonly unsafe struct EcsValuePoolNative + { + private readonly EcsValuePoolSharedStore* _store; + } + + + /// Pool for IEcsValueComponent components +#if ENABLE_IL2CPP + [Il2CppSetOption(Option.NullChecks, false)] +#endif + [MetaColor(MetaColor.DragonRose)] + [MetaGroup(EcsConsts.PACK_GROUP, EcsConsts.POOLS_GROUP)] + [MetaDescription(EcsConsts.AUTHOR, "Pool for IEcsValueComponent components.")] + [MetaID("DragonECS_C501547C9201A4B03FC25632E4FAAFD7")] + [DebuggerDisplay("Count: {Count} Type: {ComponentType}")] + public sealed unsafe class EcsValuePool : IEcsPoolImplementation, IEcsStructPool, IEnumerable //IEnumerable - IntelliSense hack + where T : unmanaged, IEcsValueComponent + { + private EcsWorld _source; + private int _componentTypeID; + private EcsMaskChunck _maskBit; + + public int* _mapping; // index = entityID / value = itemIndex;/ value = 0 = no entityID. + public void* _items; // dense; _items[0] - fake component. + public int _itemsLength; + public int _itemsCount; + public int* _recycledItems; + public int _recycledItemsLength; + public int _recycledItemsCount; + private readonly EcsValuePoolSharedStore* _sharedStore; + + private readonly IEcsComponentLifecycle _componentLifecycleHandler = EcsComponentLifecycleHandler.instance; + private readonly bool _isHasComponentLifecycleHandler = EcsComponentLifecycleHandler.isHasHandler; + private readonly IEcsComponentCopy _componentCopyHandler = EcsComponentCopyHandler.instance; + private readonly bool _isHasComponentCopyHandler = EcsComponentCopyHandler.isHasHandler; + + private bool _isLocked; + + private EcsWorld.PoolsMediator _mediator; + + #region Properites + public int Count + { + get { return _itemsCount; } + } + public int Capacity + { + get { return _itemsLength; } + } + public int ComponentTypeID + { + get { return _componentTypeID; } + } + public Type ComponentType + { + get { return typeof(T); } + } + public EcsWorld World + { + get { return _source; } + } + public bool IsReadOnly + { + get { return false; } + } + public ref T this[int index] + { + [MethodImpl(MethodImplOptions.AggressiveInlining)] + get { return ref Get(index); } + } + #endregion + + #region Constructors/Init/Destroy + public EcsValuePool() + { + if (_sharedStore == null) + { + _sharedStore = MemoryAllocator.Alloc(1).As(); + } + } + public EcsValuePool(int capacity, int recycledCapacity = -1) + { + if (_sharedStore == null) + { + _sharedStore = MemoryAllocator.Alloc(1).As(); + } + capacity = ArrayUtility.NextPow2(capacity); + if (recycledCapacity < 0) + { + recycledCapacity = capacity / 2; + } + _itemsLength = capacity; + _items = MemoryAllocator.Alloc(_itemsLength).As(); + _sharedStore->_items = _items; + _recycledItemsLength = recycledCapacity; + _recycledItems = MemoryAllocator.Alloc(_recycledItemsLength).As(); + } + void IEcsPoolImplementation.OnInit(EcsWorld world, EcsWorld.PoolsMediator mediator, int componentTypeID) + { + _source = world; + _mediator = mediator; + _componentTypeID = componentTypeID; + _maskBit = EcsMaskChunck.FromID(componentTypeID); + + _mapping = MemoryAllocator.Alloc(world.Capacity).As(); + _sharedStore->_mapping = _mapping; + var worldConfig = world.Configs.GetWorldConfigOrDefault(); + if (_items == null) + { + _itemsLength = ArrayUtility.NextPow2(worldConfig.PoolComponentsCapacity); + _items = MemoryAllocator.Alloc(_itemsLength).As(); + _sharedStore->_items = _items; + } + if (_recycledItems == null) + { + _recycledItemsLength = worldConfig.PoolRecycledComponentsCapacity; + _recycledItems = MemoryAllocator.Alloc(_recycledItemsLength).As(); + } + } + void IEcsPoolImplementation.OnWorldDestroy() { } + ~EcsValuePool() + { + MemoryAllocator.Free(_mapping); + MemoryAllocator.Free(_items); + MemoryAllocator.Free(_recycledItems); + } + #endregion + + #region Methods + public ref T Add(int entityID) + { + ref int itemIndex = ref _mapping[entityID]; +#if DEBUG + if (entityID == EcsConsts.NULL_ENTITY_ID) { Throw.Ent_ThrowIsNotAlive(_source, entityID); } + if (_source.IsUsed(entityID) == false) { Throw.Ent_ThrowIsNotAlive(_source, entityID); } + if (itemIndex > 0) { EcsPoolThrowHelper.ThrowAlreadyHasComponent(entityID); } + if (_isLocked) { EcsPoolThrowHelper.ThrowPoolLocked(); } +#elif DRAGONECS_STABILITY_MODE + if (itemIndex > 0) { return ref Get(entityID); } + if (_isLocked | _source.IsUsed(entityID) == false) { return ref _items[0]; } +#endif + if (_recycledItemsCount > 0) + { + itemIndex = _recycledItems[--_recycledItemsCount]; + _itemsCount++; + } + else + { + itemIndex = ++_itemsCount; + if (itemIndex >= _itemsLength) + { + _itemsLength = ArrayUtility.NextPow2(_itemsLength << 1); + _items = MemoryAllocator.Realloc(_items, _itemsLength).As(); + _sharedStore->_items = _items; + } + } + _mediator.RegisterComponent(entityID, _componentTypeID, _maskBit); + ref T result = ref ((T*)_items)[itemIndex]; + EnableComponent(ref result); + return ref result; + } + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public ref T Get(int entityID) + { +#if DEBUG // не нужен STAB_MODE + if (!Has(entityID)) { EcsPoolThrowHelper.ThrowNotHaveComponent(entityID); } +#endif + return ref ((T*)_items)[_mapping[entityID]]; + } + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public ref readonly T Read(int entityID) + { +#if DEBUG // не нужен STAB_MODE + if (!Has(entityID)) { EcsPoolThrowHelper.ThrowNotHaveComponent(entityID); } +#endif + return ref ((T*)_items)[_mapping[entityID]]; + } + public ref T TryAddOrGet(int entityID) + { +#if DEBUG + if (entityID == EcsConsts.NULL_ENTITY_ID) { Throw.Ent_ThrowIsNotAlive(_source, entityID); } +#endif + ref int itemIndex = ref _mapping[entityID]; + if (itemIndex <= 0) + { //Add block +#if DEBUG + if (_isLocked) { EcsPoolThrowHelper.ThrowPoolLocked(); } +#elif DRAGONECS_STABILITY_MODE + if (_isLocked) { return ref _items[0]; } +#endif + if (_recycledItemsCount > 0) + { + itemIndex = _recycledItems[--_recycledItemsCount]; + _itemsCount++; + } + else + { + itemIndex = ++_itemsCount; + if (itemIndex >= _itemsLength) + { + _itemsLength = ArrayUtility.NextPow2(_itemsLength << 1); + _items = MemoryAllocator.Realloc(_items, _itemsLength).As(); + _sharedStore->_items = _items; + } + } + _mediator.RegisterComponent(entityID, _componentTypeID, _maskBit); + EnableComponent(ref ((T*)_items)[itemIndex]); + } //Add block end + return ref ((T*)_items)[itemIndex]; + } + [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 + if (entityID == EcsConsts.NULL_ENTITY_ID) { Throw.Ent_ThrowIsNotAlive(_source, entityID); } + if (itemIndex <= 0) { EcsPoolThrowHelper.ThrowNotHaveComponent(entityID); } + if (_isLocked) { EcsPoolThrowHelper.ThrowPoolLocked(); } +#elif DRAGONECS_STABILITY_MODE + if (itemIndex <= 0) { return; } + if (_isLocked) { return; } +#endif + DisableComponent(ref ((T*)_items)[itemIndex]); + if (_recycledItemsCount >= _recycledItemsLength) + { + _recycledItemsLength = ArrayUtility.NextPow2(_recycledItemsLength << 1); + _recycledItems = MemoryAllocator.Realloc(_recycledItems, _recycledItemsLength).As(); + } + _recycledItems[_recycledItemsCount++] = itemIndex; + itemIndex = 0; + _itemsCount--; + _mediator.UnregisterComponent(entityID, _componentTypeID, _maskBit); + } + public void TryDel(int entityID) + { + if (Has(entityID)) + { + Del(entityID); + } + } + public void Copy(int fromEntityID, int toEntityID) + { +#if DEBUG + if (!Has(fromEntityID)) { EcsPoolThrowHelper.ThrowNotHaveComponent(fromEntityID); } +#elif DRAGONECS_STABILITY_MODE + if (!Has(fromEntityID)) { return; } +#endif + CopyComponent(ref Get(fromEntityID), ref TryAddOrGet(toEntityID)); + } + public void Copy(int fromEntityID, EcsWorld toWorld, int toEntityID) + { +#if DEBUG + if (!Has(fromEntityID)) { EcsPoolThrowHelper.ThrowNotHaveComponent(fromEntityID); } +#elif DRAGONECS_STABILITY_MODE + if (!Has(fromEntityID)) { return; } +#endif + CopyComponent(ref Get(fromEntityID), ref toWorld.GetPool().TryAddOrGet(toEntityID)); + } + + public void ClearAll() + { +#if DEBUG + if (_isLocked) { EcsPoolThrowHelper.ThrowPoolLocked(); } +#elif DRAGONECS_STABILITY_MODE + if (_isLocked) { return; } +#endif + _recycledItemsCount = 0; // спереди чтобы обнулялось, так как Del не обнуляет + if (_itemsCount <= 0) { return; } + var span = _source.Where(out SinglePoolAspect> _); + foreach (var entityID in span) + { + ref int itemIndex = ref _mapping[entityID]; + DisableComponent(ref ((T*)_items)[itemIndex]); + itemIndex = 0; + _mediator.UnregisterComponent(entityID, _componentTypeID, _maskBit); + } + _itemsCount = 0; + _recycledItemsCount = 0; + } + #endregion + + #region Callbacks + void IEcsPoolImplementation.OnWorldResize(int newSize) + { + _mapping = MemoryAllocator.Realloc(_mapping, newSize).As(); + _sharedStore->_mapping = _mapping; + } + void IEcsPoolImplementation.OnReleaseDelEntityBuffer(ReadOnlySpan buffer) + { + if (_itemsCount <= 0) + { + return; + } + foreach (var entityID in buffer) + { + TryDel(entityID); + } + } + void IEcsPoolImplementation.OnLockedChanged_Debug(bool locked) { _isLocked = locked; } + #endregion + + #region Other + void IEcsPool.AddEmpty(int entityID) { Add(entityID); } + void IEcsPool.AddRaw(int entityID, object dataRaw) + { + Add(entityID) = dataRaw == null ? default : (T)dataRaw; + } + object IEcsReadonlyPool.GetRaw(int entityID) { return Read(entityID); } + void IEcsPool.SetRaw(int entityID, object dataRaw) + { + Get(entityID) = dataRaw == null ? default : (T)dataRaw; + } + #endregion + + #region Listeners +#if !DRAGONECS_DISABLE_POOLS_EVENTS + public void AddListener(IEcsPoolEventListener listener) + { + EcsDebug.PrintWarning($"the {nameof(AddListener)} method is not supported for the {nameof(EcsValuePool)}."); + } + public void RemoveListener(IEcsPoolEventListener listener) + { + EcsDebug.PrintWarning($"the {nameof(RemoveListener)} method is not supported for the {nameof(EcsValuePool)}."); + } +#endif + #endregion + + #region Enable/Disable/Copy + [MethodImpl(MethodImplOptions.AggressiveInlining)] + private void EnableComponent(ref T component) + { + if (_isHasComponentLifecycleHandler) + { + _componentLifecycleHandler.Enable(ref component); + } + else + { + component = default; + } + } + [MethodImpl(MethodImplOptions.AggressiveInlining)] + private void DisableComponent(ref T component) + { + if (_isHasComponentLifecycleHandler) + { + _componentLifecycleHandler.Disable(ref component); + } + else + { + component = default; + } + } + [MethodImpl(MethodImplOptions.AggressiveInlining)] + private void CopyComponent(ref T from, ref T to) + { + if (_isHasComponentCopyHandler) + { + _componentCopyHandler.Copy(ref from, ref to); + } + else + { + to = from; + } + } + #endregion + + #region IEnumerator - IntelliSense hack + IEnumerator IEnumerable.GetEnumerator() { throw new NotImplementedException(); } + IEnumerator IEnumerable.GetEnumerator() { throw new NotImplementedException(); } + #endregion + + #region Convertors + public static implicit operator EcsValuePool(IncludeMarker a) { return a.GetInstance>(); } + public static implicit operator EcsValuePool(ExcludeMarker a) { return a.GetInstance>(); } + public static implicit operator EcsValuePool(AnyMarker a) { return a.GetInstance>(); } + public static implicit operator EcsValuePool(OptionalMarker a) { return a.GetInstance>(); } + public static implicit operator EcsValuePool(EcsWorld.GetPoolInstanceMarker a) { return a.GetInstance>(); } + #endregion + + #region Apply + public static void Apply(ref T component, int entityID, short worldID) + { + EcsWorld.GetPoolInstance>(worldID).TryAddOrGet(entityID) = component; + } + public static void Apply(ref T component, int entityID, EcsValuePool pool) + { + pool.TryAddOrGet(entityID) = component; + } + #endregion + } + +#if ENABLE_IL2CPP + [Il2CppSetOption(Option.NullChecks, false)] +#endif + [EditorBrowsable(EditorBrowsableState.Never)] + public readonly struct ReadonlyEcsValuePool : IEcsReadonlyPool //IEnumerable - IntelliSense hack + where T : unmanaged, IEcsValueComponent + { + private readonly EcsValuePool _pool; + + #region Properties + public int ComponentTypeID + { + [MethodImpl(MethodImplOptions.AggressiveInlining)] + get { return _pool.ComponentTypeID; } + } + public Type ComponentType + { + [MethodImpl(MethodImplOptions.AggressiveInlining)] + get { return _pool.ComponentType; } + } + public EcsWorld World + { + [MethodImpl(MethodImplOptions.AggressiveInlining)] + get { return _pool.World; } + } + public int Count + { + [MethodImpl(MethodImplOptions.AggressiveInlining)] + get { return _pool.Count; } + } + public bool IsReadOnly + { + [MethodImpl(MethodImplOptions.AggressiveInlining)] + get { return _pool.IsReadOnly; } + } + public ref readonly T this[int entityID] + { + get { return ref _pool.Read(entityID); } + } + #endregion + + #region Constructors + internal ReadonlyEcsValuePool(EcsValuePool pool) + { + _pool = pool; + } + #endregion + + #region Methods + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public bool Has(int entityID) { return _pool.Has(entityID); } + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public ref readonly T Get(int entityID) { return ref _pool.Read(entityID); } + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public ref readonly T Read(int entityID) { return ref _pool.Read(entityID); } + object IEcsReadonlyPool.GetRaw(int entityID) { return _pool.Read(entityID); } + +#if !DRAGONECS_DISABLE_POOLS_EVENTS + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public void AddListener(IEcsPoolEventListener listener) { _pool.AddListener(listener); } + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public void RemoveListener(IEcsPoolEventListener listener) { _pool.AddListener(listener); } +#endif + #endregion + + #region Convertors + public static implicit operator ReadonlyEcsValuePool(EcsValuePool a) { return new ReadonlyEcsValuePool(a); } + public static implicit operator ReadonlyEcsValuePool(IncludeMarker a) { return a.GetInstance>(); } + public static implicit operator ReadonlyEcsValuePool(ExcludeMarker a) { return a.GetInstance>(); } + public static implicit operator ReadonlyEcsValuePool(AnyMarker a) { return a.GetInstance>(); } + public static implicit operator ReadonlyEcsValuePool(OptionalMarker a) { return a.GetInstance>(); } + public static implicit operator ReadonlyEcsValuePool(EcsWorld.GetPoolInstanceMarker a) { return a.GetInstance>(); } + #endregion + } + + + public static class EcsValuePoolExtensions + { + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static EcsValuePool GetPool(this EcsWorld self) where TComponent : unmanaged, IEcsValueComponent + { + return self.GetPoolInstance>(); + } + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static EcsValuePool GetPoolUnchecked(this EcsWorld self) where TComponent : unmanaged, IEcsValueComponent + { + return self.GetPoolInstanceUnchecked>(); + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static EcsValuePool Inc(this EcsAspect.Builder self) where TComponent : unmanaged, IEcsValueComponent + { + return self.IncludePool>(); + } + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static EcsValuePool Exc(this EcsAspect.Builder self) where TComponent : unmanaged, IEcsValueComponent + { + return self.ExcludePool>(); + } + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static EcsValuePool Opt(this EcsAspect.Builder self) where TComponent : unmanaged, IEcsValueComponent + { + return self.OptionalPool>(); + } + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static EcsValuePool Any(this EcsAspect.Builder self) where TComponent : unmanaged, IEcsValueComponent + { + return self.AnyPool>(); + } + } +} \ No newline at end of file