Optimization

This commit is contained in:
Mikhail 2023-04-01 20:45:37 +08:00
parent a489f4cbae
commit de7762861c
12 changed files with 525 additions and 202 deletions

View File

@ -22,12 +22,12 @@
public sealed class EcsPreInitRunner : EcsRunner<IEcsPreInitSystem>, IEcsPreInitSystem
{
#if DEBUG
#if DEBUG && !DISABLE_DRAGONECS_DEBUG
private EcsProfilerMarker[] _markers;
#endif
public void PreInit(EcsPipeline pipeline)
{
#if DEBUG
#if DEBUG && !DISABLE_DRAGONECS_DEBUG
for (int i = 0; i < targets.Length; i++)
{
using (_markers[i].Auto())
@ -38,7 +38,7 @@
#endif
}
#if DEBUG
#if DEBUG && !DISABLE_DRAGONECS_DEBUG
protected override void OnSetup()
{
_markers = new EcsProfilerMarker[targets.Length];
@ -51,12 +51,12 @@
}
public sealed class EcsInitRunner : EcsRunner<IEcsInitSystem>, IEcsInitSystem
{
#if DEBUG
#if DEBUG && !DISABLE_DRAGONECS_DEBUG
private EcsProfilerMarker[] _markers;
#endif
public void Init(EcsPipeline pipeline)
{
#if DEBUG
#if DEBUG && !DISABLE_DRAGONECS_DEBUG
for (int i = 0; i < targets.Length; i++)
{
using (_markers[i].Auto())
@ -67,7 +67,7 @@
#endif
}
#if DEBUG
#if DEBUG && !DISABLE_DRAGONECS_DEBUG
protected override void OnSetup()
{
_markers = new EcsProfilerMarker[targets.Length];
@ -80,12 +80,12 @@
}
public sealed class EcsRunRunner : EcsRunner<IEcsRunSystem>, IEcsRunSystem
{
#if DEBUG
#if DEBUG && !DISABLE_DRAGONECS_DEBUG
private EcsProfilerMarker[] _markers;
#endif
public void Run(EcsPipeline pipeline)
{
#if DEBUG
#if DEBUG && !DISABLE_DRAGONECS_DEBUG
for (int i = 0; i < targets.Length; i++)
{
using (_markers[i].Auto())
@ -97,7 +97,7 @@
#endif
}
#if DEBUG
#if DEBUG && !DISABLE_DRAGONECS_DEBUG
protected override void OnSetup()
{
_markers = new EcsProfilerMarker[targets.Length];
@ -110,12 +110,12 @@
}
public sealed class EcsDestroyRunner : EcsRunner<IEcsDestroySystem>, IEcsDestroySystem
{
#if DEBUG
#if DEBUG && !DISABLE_DRAGONECS_DEBUG
private EcsProfilerMarker[] _markers;
#endif
public void Destroy(EcsPipeline pipeline)
{
#if DEBUG
#if DEBUG && !DISABLE_DRAGONECS_DEBUG
for (int i = 0; i < targets.Length; i++)
{
using (_markers[i].Auto())
@ -126,7 +126,7 @@
#endif
}
#if DEBUG
#if DEBUG && !DISABLE_DRAGONECS_DEBUG
protected override void OnSetup()
{
_markers = new EcsProfilerMarker[targets.Length];

View File

@ -0,0 +1,87 @@
namespace DCFApixels.DragonECS
{
public interface IEcsComponentAdd : IEcsSystem
{
public void OnComponentAdd<T>(int entityID);
}
public interface IEcsComponentWrite : IEcsSystem
{
public void OnComponentWrite<T>(int entityID);
}
public interface IEcsComponentDel : IEcsSystem
{
public void OnComponentDel<T>(int entityID);
}
public interface IEcsComponentLifecycle : IEcsComponentAdd, IEcsComponentWrite, IEcsComponentDel { }
public sealed class EcsEntityAddComponentRunner : EcsRunner<IEcsComponentAdd>, IEcsComponentAdd
{
public void OnComponentAdd<T>(int entityID)
{
foreach (var item in targets) item.OnComponentAdd<T>(entityID);
}
}
public sealed class EcsEntityChangeComponentRunner : EcsRunner<IEcsComponentWrite>, IEcsComponentWrite
{
public void OnComponentWrite<T>(int entityID)
{
foreach (var item in targets) item.OnComponentWrite<T>(entityID);
}
}
public sealed class EcsEntityDelComponentRunner : EcsRunner<IEcsComponentDel>, IEcsComponentDel
{
public void OnComponentDel<T>(int entityID)
{
foreach (var item in targets) item.OnComponentDel<T>(entityID);
}
}
public interface IEcsEntityCreate : IEcsSystem
{
public void OnEntityCreate(ent entity);
}
public interface IEcsEntityDestroy : IEcsSystem
{
public void OnEntityDestroy(ent entity);
}
public interface IEcsEntityLifecycle : IEcsEntityCreate, IEcsEntityDestroy { }
public sealed class EcsEntityCreateRunner : EcsRunner<IEcsEntityCreate>, IEcsEntityCreate
{
public void OnEntityCreate(ent entity)
{
foreach (var item in targets) item.OnEntityCreate(entity);
}
}
public sealed class EcsEntityDestroyRunner : EcsRunner<IEcsEntityDestroy>, IEcsEntityDestroy
{
public void OnEntityDestroy(ent entity)
{
foreach (var item in targets) item.OnEntityDestroy(entity);
}
}
public interface IEcsWorldCreate : IEcsSystem
{
public void OnWorldCreate(IEcsWorld world);
}
public interface IEcsWorldDestroy : IEcsSystem
{
public void OnWorldDestroy(IEcsWorld world);
}
public interface IEcsWorldLifecycle : IEcsWorldCreate, IEcsWorldDestroy { }
public sealed class EcsWorldCreateRunner : EcsRunner<IEcsWorldCreate>, IEcsWorldCreate
{
public void OnWorldCreate(IEcsWorld world)
{
foreach (var item in targets) item.OnWorldCreate(world);
}
}
public sealed class EcsWorldDestryRunner : EcsRunner<IEcsWorldDestroy>, IEcsWorldDestroy
{
public void OnWorldDestroy(IEcsWorld world)
{
foreach (var item in targets) item.OnWorldDestroy(world);
}
}
}

View File

@ -1,5 +1,11 @@
namespace DCFApixels.DragonECS
{
public sealed class EcsDefaultWrold : EcsWorld<EcsDefaultWrold> { }
public sealed class EcsEventWrold : EcsWorld<EcsDefaultWrold> { }
public sealed class EcsDefaultWrold : EcsWorld<EcsDefaultWrold>
{
public EcsDefaultWrold(EcsPipeline pipeline) : base(pipeline) { }
}
public sealed class EcsEventWrold : EcsWorld<EcsDefaultWrold>
{
public EcsEventWrold(EcsPipeline pipeline) : base(pipeline) { }
}
}

View File

@ -17,7 +17,7 @@ namespace DCFApixels.DragonECS
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public AutoScope Auto() => new AutoScope(id);
public readonly struct AutoScope : IDisposable
public readonly ref struct AutoScope
{
private readonly int id;
public AutoScope(int id)
@ -43,14 +43,14 @@ namespace DCFApixels.DragonECS
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static void Print(string tag, object v)
{
#if !DISABLE_ECS_DEBUG
#if !DISABLE_DRAGONECS_DEBUG
DebugService.Instance.Print(tag, v);
#endif
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static int RegisterMark(string name)
{
#if !DISABLE_ECS_DEBUG
#if !DISABLE_DRAGONECS_DEBUG
return DebugService.Instance.RegisterMark(name);
#else
return 0;
@ -59,21 +59,21 @@ namespace DCFApixels.DragonECS
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static void DeleteMark(string name)
{
#if !DISABLE_ECS_DEBUG
#if !DISABLE_DRAGONECS_DEBUG
DebugService.Instance.DeleteMark(name);
#endif
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static void ProfileMarkBegin(int id)
{
#if !DISABLE_ECS_DEBUG
#if !DISABLE_DRAGONECS_DEBUG
DebugService.Instance.ProfileMarkBegin(id);
#endif
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static void ProfileMarkEnd(int id)
{
#if !DISABLE_ECS_DEBUG
#if !DISABLE_DRAGONECS_DEBUG
DebugService.Instance.ProfileMarkEnd(id);
#endif
}
@ -145,7 +145,7 @@ namespace DCFApixels.DragonECS
public DefaultDebugService()
{
#if !DISABLE_ECS_DEBUG
#if !DISABLE_DRAGONECS_DEBUG
_stopwatchs = new Stopwatch[64];
_stopwatchsNames= new string[64];
#endif

View File

@ -15,7 +15,7 @@ namespace DCFApixels.DragonECS
public class EcsFilter : IEcsFilter
{
private readonly IEcsWorld _source;
private readonly EcsGroup _entities;
internal readonly EcsGroup entities;
private readonly EcsMask _mask;
#region Properties
@ -32,12 +32,12 @@ namespace DCFApixels.DragonECS
public EcsReadonlyGroup Entities
{
[MethodImpl(MethodImplOptions.AggressiveInlining)]
get => _entities.Readonly;
get => entities.Readonly;
}
public int EntitiesCount
{
[MethodImpl(MethodImplOptions.AggressiveInlining)]
get => _entities.Count;
get => entities.Count;
}
#endregion
@ -46,7 +46,7 @@ namespace DCFApixels.DragonECS
{
_source = source;
_mask = mask;
_entities = new EcsGroup(source, capasity);
entities = new EcsGroup(source, capasity);
}
#endregion
@ -54,19 +54,19 @@ namespace DCFApixels.DragonECS
[MethodImpl(MethodImplOptions.AggressiveInlining)]
internal void Add(int entityID)
{
_entities.Add(entityID);
entities.UncheckedAdd(entityID);
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
internal void Remove(int entityID)
{
_entities.Remove(entityID);
entities.UncheckedRemove(entityID);
}
#endregion
#region GetEnumerator
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public EcsGroup.Enumerator GetEnumerator() => _entities.GetEnumerator();
public EcsGroup.Enumerator GetEnumerator() => entities.GetEnumerator();
#endregion
}
}

View File

@ -17,6 +17,16 @@ namespace DCFApixels.DragonECS
[MethodImpl(MethodImplOptions.AggressiveInlining)]
get => _source.Count;
}
public int CapacityDense
{
[MethodImpl(MethodImplOptions.AggressiveInlining)]
get => _source.CapacityDense;
}
public int CapacitySparce
{
[MethodImpl(MethodImplOptions.AggressiveInlining)]
get => _source.CapacitySparce;
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public bool Contains(int entityID) => _source.Contains(entityID);
[MethodImpl(MethodImplOptions.AggressiveInlining)]
@ -51,6 +61,16 @@ namespace DCFApixels.DragonECS
[MethodImpl(MethodImplOptions.AggressiveInlining)]
get => _count;
}
public int CapacityDense
{
[MethodImpl(MethodImplOptions.AggressiveInlining)]
get => _dense.Length;
}
public int CapacitySparce
{
[MethodImpl(MethodImplOptions.AggressiveInlining)]
get => _sparse.Length;
}
public EcsReadonlyGroup Readonly
{
[MethodImpl(MethodImplOptions.AggressiveInlining)]
@ -60,11 +80,29 @@ namespace DCFApixels.DragonECS
#region Constrcutors
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public EcsGroup(IEcsWorld source, int denseCapacity = 64, int sparseCapacity = 256, int delayedOpsCapacity = 128)
internal EcsGroup(IEcsWorld source, int denseCapacity, int sparceCapacity, int delayedOpsCapacity)
{
_source = source;
source.RegisterGroup(this);
_dense = new int[denseCapacity];
_sparse = new int[sparseCapacity];
_sparse = new int[sparceCapacity];
_delayedOps = new delayedOp[delayedOpsCapacity];
_lockCount = 0;
_delayedOpsCount = 0;
_count = 0;
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public EcsGroup(IEcsWorld source, int denseCapacity = 64, int delayedOpsCapacity = 128)
{
_source = source;
source.RegisterGroup(this);
_dense = new int[denseCapacity];
_sparse = new int[source.Entities.CapacitySparce];
_delayedOps = new delayedOp[delayedOpsCapacity];
@ -79,12 +117,29 @@ namespace DCFApixels.DragonECS
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public bool Contains(int entityID)
{
return /*entityID > 0 && */ entityID < _sparse.Length && _sparse[entityID] > 0;
//TODO добавить проверку на больше 0 в #if (DEBUG && !DISABLE_DRAGONECS_DEBUG) || !DRAGONECS_NO_SANITIZE_CHECKS
#if (DEBUG && !DISABLE_DRAGONECS_DEBUG) || !DRAGONECS_NO_SANITIZE_CHECKS
#endif
return /*entityID > 0 && entityID < _sparse.Length && */ _sparse[entityID] > 0;
}
#endregion
#region IndexOf
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public int IndexOf(int entityID)
{
return _sparse[entityID];
}
#endregion
#region add/remove
public void UncheckedAdd(int entityID) => AddInternal(entityID);
public void Add(int entityID)
{
if (Contains(entityID)) return;
Add(entityID);
}
private void AddInternal(int entityID)
{
if (_lockCount > 0)
{
@ -92,40 +147,34 @@ namespace DCFApixels.DragonECS
return;
}
if (Contains(entityID))
return;
if(++_count >= _dense.Length)
if (++_count >= _dense.Length)
Array.Resize(ref _dense, _dense.Length << 1);
if (entityID >= _sparse.Length)
{
int neadedSpace = _sparse.Length;
while (entityID >= neadedSpace)
neadedSpace <<= 1;
Array.Resize(ref _sparse, neadedSpace);
}
_dense[_count] = entityID;
_sparse[entityID] = _count;
}
public void UncheckedRemove(int entityID) => RemoveInternal(entityID);
public void Remove(int entityID)
{
if (!Contains(entityID)) return;
RemoveInternal(entityID);
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
private void RemoveInternal(int entityID)
{
if (_lockCount > 0)
{
AddDelayedOp(entityID, DEALAYED_REMOVE);
return;
}
if (!Contains(entityID))
return;
_dense[_sparse[entityID]] = _dense[_count];
_sparse[_dense[_count--]] = _sparse[entityID];
_sparse[entityID] = 0;
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
private void AddDelayedOp(int entityID, int isAddBitFlag)
{
@ -137,24 +186,32 @@ namespace DCFApixels.DragonECS
}
#endregion
//TODO добавить автосоритровку при каждом GetEnumerator
internal void OnWorldResize(int newSize)
{
Array.Resize(ref _sparse, newSize);
}
//TODO добавить метод Sort
//TODO добавить автосоритровку при каждом GetEnumerator и проверить будет ли прирост производительности или ее падение.
//Суть в том что так возможно можно будет более плотно подавать данные в проц
#region AddGroup/RemoveGroup
public void AddGroup(EcsReadonlyGroup group)
{
foreach (var item in group) Add(item.id);
foreach (var item in group) UncheckedAdd(item.id);
}
public void RemoveGroup(EcsReadonlyGroup group)
{
foreach (var item in group) Remove(item.id);
foreach (var item in group) UncheckedRemove(item.id);
}
public void AddGroup(EcsGroup group)
{
foreach (var item in group) Add(item.id);
foreach (var item in group) UncheckedAdd(item.id);
}
public void RemoveGroup(EcsGroup group)
{
foreach (var item in group) Remove(item.id);
foreach (var item in group) UncheckedRemove(item.id);
}
#endregion
@ -162,7 +219,7 @@ namespace DCFApixels.DragonECS
[MethodImpl(MethodImplOptions.AggressiveInlining)]
private void Unlock()
{
#if DEBUG || !DRAGONECS_NO_SANITIZE_CHECKS
#if (DEBUG && !DISABLE_DRAGONECS_DEBUG) || !DRAGONECS_NO_SANITIZE_CHECKS
if (_lockCount <= 0)
{
throw new Exception($"Invalid lock-unlock balance for {nameof(EcsGroup)}.");
@ -175,11 +232,11 @@ namespace DCFApixels.DragonECS
delayedOp op = _delayedOps[i];
if (op >= 0) //delayedOp.IsAdded
{
Add(op & int.MaxValue); //delayedOp.Entity
UncheckedAdd(op & int.MaxValue); //delayedOp.Entity
}
else
{
Remove(op & int.MaxValue); //delayedOp.Entity
UncheckedRemove(op & int.MaxValue); //delayedOp.Entity
}
}
}
@ -214,7 +271,6 @@ namespace DCFApixels.DragonECS
{
using (_marker.Auto())
return _source.World.GetEntity(_source._dense[_pointer]);
// return _source._dense[_pointer];
}
}
@ -238,4 +294,19 @@ namespace DCFApixels.DragonECS
}
#endregion
}
public static class EcsGroupExtensions
{
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static void Normalize<T>(this EcsGroup self, ref T[] array)
{
if (array.Length < self.CapacityDense) Array.Resize(ref array, self.CapacityDense);
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static void Normalize<T>(this EcsReadonlyGroup self, ref T[] array)
{
if (array.Length < self.CapacityDense) Array.Resize(ref array, self.CapacityDense);
}
}
}

View File

@ -7,6 +7,21 @@ namespace DCFApixels.DragonECS
{
public sealed class EcsPipeline
{
private static EcsPipeline _empty;
public static EcsPipeline Empty
{
get
{
if(_empty == null)
{
_empty = new EcsPipeline(Array.Empty<IEcsSystem>());
_empty.Init();
_empty._isEmptyDummy = true;
}
return _empty;
}
}
private IEcsSystem[] _allSystems;
private Dictionary<Type, IEcsRunner> _runners;
private IEcsRunSystem _runRunnerCache;
@ -16,10 +31,12 @@ namespace DCFApixels.DragonECS
private bool _isInit;
private bool _isDestoryed;
private bool _isEmptyDummy;
#region Properties
public ReadOnlyCollection<IEcsSystem> AllSystems => _allSystemsSealed;
public ReadOnlyDictionary<Type, IEcsRunner> AllRunners => _allRunnersSealed;
public bool IsInit => _isInit;
public bool IsDestoryed => _isDestoryed;
#endregion
@ -33,6 +50,7 @@ namespace DCFApixels.DragonECS
_allRunnersSealed = new ReadOnlyDictionary<Type, IEcsRunner>(_runners);
_isInit = false;
_isEmptyDummy = false;
_isDestoryed = false;
}
#endregion
@ -57,7 +75,10 @@ namespace DCFApixels.DragonECS
#region LifeCycle
public void Init()
{
if(_isInit == true)
if (_isEmptyDummy)
return;
if (_isInit == true)
{
EcsDebug.Print("[Warning]", $"This {nameof(EcsPipeline)} has already been initialized");
return;
@ -80,7 +101,7 @@ namespace DCFApixels.DragonECS
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public void Run()
{
#if DEBUG || !DRAGONECS_NO_SANITIZE_CHECKS
#if (DEBUG && !DISABLE_DRAGONECS_DEBUG) || !DRAGONECS_NO_SANITIZE_CHECKS
CheckBeforeInitForMethod(nameof(Run));
CheckAfterDestroyForMethod(nameof(Run));
#endif
@ -88,7 +109,10 @@ namespace DCFApixels.DragonECS
}
public void Destroy()
{
#if DEBUG || !DRAGONECS_NO_SANITIZE_CHECKS
if (_isEmptyDummy)
return;
#if (DEBUG && !DISABLE_DRAGONECS_DEBUG) || !DRAGONECS_NO_SANITIZE_CHECKS
CheckBeforeInitForMethod(nameof(Run));
#endif
if (_isDestoryed == true)
@ -102,7 +126,7 @@ namespace DCFApixels.DragonECS
#endregion
#region StateChecks
#if DEBUG || !DRAGONECS_NO_SANITIZE_CHECKS
#if (DEBUG && !DISABLE_DRAGONECS_DEBUG) || !DRAGONECS_NO_SANITIZE_CHECKS
private void CheckBeforeInitForMethod(string methodName)
{
if (!_isInit)

View File

@ -1,6 +1,8 @@
using System;
using System.Linq;
using System.Runtime.CompilerServices;
using System.Runtime.InteropServices;
using Unity.Profiling;
namespace DCFApixels.DragonECS
{
@ -15,6 +17,8 @@ namespace DCFApixels.DragonECS
public bool Has(int index);
public void Write(int index);
public void Del(int index);
internal void OnWorldResize(int newSize);
}
public interface IEcsPool<T> : IEcsPool
@ -43,6 +47,8 @@ namespace DCFApixels.DragonECS
public void Del(int index) { }
public bool Has(int index) => false;
public void Write(int index) { }
void IEcsPool.OnWorldResize(int newSize) { }
}
public class EcsPool<T> : IEcsPool<T>
@ -50,14 +56,21 @@ namespace DCFApixels.DragonECS
{
private readonly int _id;
private readonly IEcsWorld _source;
private readonly SparseSet _sparseSet;
private T[] _denseItems;
// private readonly EcsGroup _entities;
private int[] _mapping;// index = entity / value = itemIndex;/ value = 0 = no entity
private T[] _items; //dense
private int _itemsCount;
private int[] _recycledItems;
private int _recycledItemsCount;
private IEcsComponentReset<T> _componentResetHandler;
private PoolRunnres _poolRunnres;
#region Properites
public int EntitiesCount => _sparseSet.Count;
public int Capacity => _sparseSet.CapacityDense;
public int EntitiesCount => _itemsCount;
public int Capacity => _items.Length;
public IEcsWorld World => _source;
public Type DataType => typeof(T);
@ -65,47 +78,81 @@ namespace DCFApixels.DragonECS
#endregion
#region Constructors
public EcsPool(IEcsWorld source, int id, int capacity)
internal EcsPool(IEcsWorld source, int id, int capacity, PoolRunnres poolRunnres)
{
_source = source;
_id = id;
_sparseSet = new SparseSet(capacity, capacity);
_denseItems =new T[capacity];
_mapping = new int[source.EntitesCapacity];
_recycledItems = new int[128];
_recycledItemsCount = 0;
_items =new T[capacity];
_itemsCount = 0;
_componentResetHandler = ComponentResetHandler.New<T>();
_poolRunnres = poolRunnres;
}
#endregion
#region Read/Write/Has/Del
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public ref readonly T Read(int entity)
#region Write/Read/Has/Del
private ProfilerMarker _writeMark = new ProfilerMarker("EcsPoo.Write");
private ProfilerMarker _readMark = new ProfilerMarker("EcsPoo.Read");
private ProfilerMarker _hasMark = new ProfilerMarker("EcsPoo.Has");
private ProfilerMarker _delMark = new ProfilerMarker("EcsPoo.Del");
public ref T Write(int entityID)
{
return ref _denseItems[_sparseSet.IndexOf(entity)];
//using (_writeMark.Auto())
//{
ref int itemIndex = ref _mapping[entityID];
if (itemIndex <= 0) //åñëè 0 òî íàäî äîáàâèòü
{
if (_recycledItemsCount > 0)
{
itemIndex = _recycledItems[--_recycledItemsCount];
_itemsCount++;
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public ref T Write(int entity)
else
{
if (!_sparseSet.Contains(entity))
{
_sparseSet.Add(entity);
_sparseSet.Normalize(ref _denseItems);
_source.OnEntityComponentAdded(entity, _id);
_componentResetHandler.Reset(ref _denseItems[_sparseSet.IndexOf(entity)]);
itemIndex = _itemsCount++;
if (itemIndex >= _items.Length)
Array.Resize(ref _items, _items.Length << 1);
}
return ref _denseItems[_sparseSet.IndexOf(entity)];
_mapping[entityID] = itemIndex;
_componentResetHandler.Reset(ref _items[itemIndex]);
_source.OnEntityComponentAdded(entityID, _id);
_poolRunnres.add.OnComponentAdd<T>(entityID);
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public bool Has(int entity)
{
return _sparseSet.IndexOf(entity) >= 0;
_poolRunnres.write.OnComponentWrite<T>(entityID);
return ref _items[itemIndex];
// }
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public void Del(int entity)
public ref readonly T Read(int entityID)
{
_sparseSet.RemoveAt(entity);
_source.OnEntityComponentRemoved(entity, _id);
//using (_readMark.Auto())
return ref _items[_mapping[entityID]];
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public bool Has(int entityID)
{
//using (_hasMark.Auto())
return _mapping[entityID] > 0;
}
public void Del(int entityID)
{
//using (_delMark.Auto())
//{
if(_recycledItemsCount >= _recycledItems.Length)
Array.Resize(ref _recycledItems, _recycledItems.Length << 1);
_recycledItems[_recycledItemsCount++] = _mapping[entityID];
_mapping[entityID] = 0;
_itemsCount--;
//_entities.UncheckedRemove(entityID);
_source.OnEntityComponentRemoved(entityID, _id);
_poolRunnres.del.OnComponentDel<T>(entityID);
//}
}
#endregion
@ -123,33 +170,10 @@ namespace DCFApixels.DragonECS
}
public override int GetHashCode() => _source.GetHashCode() + ID;
#endregion
}
#region ComponentResetHandler
internal static class ComponentResetHandler
void IEcsPool.OnWorldResize(int newSize)
{
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static IEcsComponentReset<T> New<T>()
{
Type targetType = typeof(T);
if (targetType.GetInterfaces().Contains(typeof(IEcsComponentReset<>).MakeGenericType(targetType)))
{
return (IEcsComponentReset<T>)Activator.CreateInstance(typeof(ComponentResetHandler<>).MakeGenericType(targetType));
}
return (IEcsComponentReset<T>)Activator.CreateInstance(typeof(ComponentResetDummy<>).MakeGenericType(targetType));
Array.Resize(ref _mapping, newSize);
}
}
internal sealed class ComponentResetDummy<T> : IEcsComponentReset<T>
{
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public void Reset(ref T component) => component = default;
}
internal sealed class ComponentResetHandler<T> : IEcsComponentReset<T>
where T : IEcsComponentReset<T>
{
private T _fakeInstnace = default;
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public void Reset(ref T component) => _fakeInstnace.Reset(ref component);
}
#endregion
}

View File

@ -51,7 +51,7 @@ namespace DCFApixels.DragonECS
.Where(type => type.BaseType != null && type.BaseType.IsGenericType && runnerBaseType == type.BaseType.GetGenericTypeDefinition()));
}
#if DEBUG || !DRAGONECS_NO_SANITIZE_CHECKS
#if (DEBUG && !DISABLE_DRAGONECS_DEBUG) || !DRAGONECS_NO_SANITIZE_CHECKS
for (int i = 0; i < runnerHandlerTypes.Count; i++)
{
var e = CheckRunnerValide(runnerHandlerTypes[i]);
@ -136,7 +136,7 @@ namespace DCFApixels.DragonECS
private static Type _subclass;
internal static void Register(Type subclass)
{
#if DEBUG || !DRAGONECS_NO_SANITIZE_CHECKS
#if (DEBUG && !DISABLE_DRAGONECS_DEBUG) || !DRAGONECS_NO_SANITIZE_CHECKS
if (_subclass != null)
{
throw new EcsRunnerImplementationException($"The Runner<{typeof(TInterface).FullName}> can have only one implementing subclass");

View File

@ -2,6 +2,8 @@
using System.Collections.Generic;
using System.Collections.ObjectModel;
using System.Runtime.CompilerServices;
using System.Runtime.InteropServices;
using System.Xml;
namespace DCFApixels.DragonECS
{
@ -12,9 +14,12 @@ namespace DCFApixels.DragonECS
public bool IsEmpty { get; }
public Type ArchetypeType { get; }
public int ID { get; }
public EcsPipeline Pipeline { get; }
public int EntitesCount { get; }
public int EntitesCapacity { get; }
public EcsReadonlyGroup Entities { get; }
#endregion
#region GetterMethods
public ReadOnlySpan<IEcsPool> GetAllPools();
@ -22,12 +27,13 @@ namespace DCFApixels.DragonECS
#region Methods
public EcsPool<T> GetPool<T>() where T : struct;
public EcsPool<T> UncheckedGetPool<T>() where T : struct;
public EcsFilter Filter<TInc>() where TInc : struct, IInc;
public EcsFilter Filter<TInc, TExc>() where TInc : struct, IInc where TExc : struct, IExc;
public ent NewEntity();
public void DelEntity(ent entity);
public bool EntityIsAlive(int entityID, short gen);
public ent GetEntity(int entityID);
public void DelEntity(int entityID);
public void Destroy();
public bool IsMaskCompatible(EcsMask mask, int entity);
@ -35,6 +41,10 @@ namespace DCFApixels.DragonECS
internal void OnEntityComponentAdded(int entityID, int changedPoolID);
internal void OnEntityComponentRemoved(int entityID, int changedPoolID);
public int GetComponentID<T>();
internal void RegisterGroup(EcsGroup group);
#endregion
}
@ -48,6 +58,8 @@ namespace DCFApixels.DragonECS
public EcsWorld()
{
id = (short)_worldIdDispenser.GetFree();
if(id >= Worlds.Length)
Array.Resize(ref Worlds, Worlds.Length << 1);
Worlds[id] = (IEcsWorld)this;
}
@ -75,8 +87,19 @@ namespace DCFApixels.DragonECS
private EcsFilter[] _filters;
private EcsPipeline _pipeline;
private List<EcsGroup> _groups;
#region RunnersCache
private PoolRunnres _poolRunnres;
private IEcsEntityCreate _entityCreate;
private IEcsEntityDestroy _entityDestry;
#endregion
#region GetterMethods
public ReadOnlySpan<IEcsPool> GetAllPools() => new ReadOnlySpan<IEcsPool>(_pools);
public int GetComponentID<T>() => ComponentType<T>.uniqueID;
#endregion
@ -84,21 +107,38 @@ namespace DCFApixels.DragonECS
public bool IsEmpty => _entities.Count < 0;
public Type ArchetypeType => typeof(TArchetype);
public int ID => id;
public EcsPipeline Pipeline => _pipeline;
public int EntitesCount => _entities.Count;
public int EntitesCapacity => _entities.CapacityDense;
public EcsReadonlyGroup Entities => _entities.Readonly;
#endregion
#region Constructors
public EcsWorld()
public EcsWorld(EcsPipeline pipline = null)
{
_pipeline = pipline ?? EcsPipeline.Empty;
if (!_pipeline.IsInit) pipline.Init();
_entityDispenser = new IntDispenser(1);
_nullPool = new EcsNullPool(this);
_pools = new IEcsPool[512];
FillArray(_pools, _nullPool);
//Array.Fill(_pools, _nullPool); //TODO Fix it
_gens = new short[512];
_filters = new EcsFilter[64];
_entities = new EcsGroup(this, 512);
_groups = new List<EcsGroup>(128);
_entities = new EcsGroup(this, 512, 512, 0);
_filtersByIncludedComponents = new List<EcsFilter>[16];
_filtersByExcludedComponents = new List<EcsFilter>[16];
_poolRunnres = new PoolRunnres(_pipeline);
_entityCreate = _pipeline.GetRunner<IEcsEntityCreate>();
_entityDestry = _pipeline.GetRunner<IEcsEntityDestroy>();
_pipeline.GetRunner<IEcsInject<TArchetype>>().Inject((TArchetype)this);
_pipeline.GetRunner<IEcsInject<IEcsWorld>>().Inject(this);
_pipeline.GetRunner<IEcsWorldCreate>().OnWorldCreate(this);
}
#endregion
@ -120,11 +160,14 @@ namespace DCFApixels.DragonECS
if (_pools[uniqueID] == _nullPool)
{
_pools[uniqueID] = new EcsPool<T>(this, ComponentType<T>.uniqueID, 512);
_pools[uniqueID] = new EcsPool<T>(this, ComponentType<T>.uniqueID, 512, _poolRunnres);
}
return (EcsPool<T>)_pools[uniqueID];
}
public EcsPool<T> UncheckedGetPool<T>() where T : struct
{
return (EcsPool<T>)_pools[ComponentType<T>.uniqueID];
}
#endregion
#region GetFilter
@ -181,7 +224,6 @@ namespace DCFApixels.DragonECS
}
}
return filter;
}
#endregion
@ -189,7 +231,7 @@ namespace DCFApixels.DragonECS
#region IsMaskCompatible/IsMaskCompatibleWithout
public bool IsMaskCompatible(EcsMask mask, int entity)
{
#if DEBUG || !DRAGONECS_NO_SANITIZE_CHECKS
#if (DEBUG && !DISABLE_DRAGONECS_DEBUG) || !DRAGONECS_NO_SANITIZE_CHECKS
if (mask.WorldArchetypeType != typeof(TArchetype))
throw new EcsFrameworkException("mask.WorldArchetypeType != typeof(TArchetype)");
#endif
@ -208,7 +250,7 @@ namespace DCFApixels.DragonECS
public bool IsMaskCompatibleWithout(EcsMask mask, int entity, int otherComponentID)
{
#if DEBUG || !DRAGONECS_NO_SANITIZE_CHECKS
#if (DEBUG && !DISABLE_DRAGONECS_DEBUG) || !DRAGONECS_NO_SANITIZE_CHECKS
if (mask.WorldArchetypeType != typeof(TArchetype))
throw new EcsFrameworkException("mask.WorldArchetypeType != typeof(TArchetype)");
#endif
@ -220,8 +262,8 @@ namespace DCFApixels.DragonECS
}
for (int i = 0, iMax = mask.ExcCount; i < iMax; i++)
{
int poolID = mask.Exc[i];
if (poolID != otherComponentID && _pools[poolID].Has(entity))
int componentID = mask.Exc[i];
if (componentID != otherComponentID && _pools[componentID].Has(entity))
return false;
}
return true;
@ -234,53 +276,59 @@ namespace DCFApixels.DragonECS
var includeList = _filtersByIncludedComponents[componentID];
var excludeList = _filtersByExcludedComponents[componentID];
if (includeList != null)
{
foreach (var filter in includeList)
{
if (IsMaskCompatible(filter.Mask, entityID))
{
filter.Add(entityID);
}
}
}
if (excludeList != null)
{
foreach (var filter in excludeList)
{
if (IsMaskCompatibleWithout(filter.Mask, entityID, componentID))
{
filter.Remove(entityID);
}
}
}
//if (includeList != null)
//{
// foreach (var filter in includeList)
// {
// if (IsMaskCompatible(filter.Mask, entityID))
// {
// filter.Add(entityID);
// }
// }
//}
//if (excludeList != null)
//{
// foreach (var filter in excludeList)
// {
// if (IsMaskCompatibleWithout(filter.Mask, entityID, componentID))
// {
// filter.Remove(entityID);
// }
// }
//}
if (includeList != null) foreach (var filter in includeList) filter.entities.Add(entityID);
if (excludeList != null) foreach (var filter in excludeList) filter.entities.Remove(entityID);
}
void IEcsWorld.OnEntityComponentRemoved(int entityID, int changedPoolID)
void IEcsWorld.OnEntityComponentRemoved(int entityID, int componentID)
{
var includeList = _filtersByIncludedComponents[changedPoolID];
var excludeList = _filtersByExcludedComponents[changedPoolID];
var includeList = _filtersByIncludedComponents[componentID];
var excludeList = _filtersByExcludedComponents[componentID];
if (includeList != null)
{
foreach (var filter in includeList)
{
if (IsMaskCompatible(filter.Mask, entityID))
{
filter.Remove(entityID);
}
}
}
if (excludeList != null)
{
foreach (var filter in excludeList)
{
if (IsMaskCompatibleWithout(filter.Mask, entityID, changedPoolID))
{
filter.Add(entityID);
}
}
}
//if (includeList != null)
//{
// foreach (var filter in includeList)
// {
// if (IsMaskCompatible(filter.Mask, entityID))
// {
// filter.Remove(entityID);
// }
// }
//}
//if (excludeList != null)
//{
// foreach (var filter in excludeList)
// {
// if (IsMaskCompatibleWithout(filter.Mask, entityID, componentID))
// {
// filter.Add(entityID);
// }
// }
//}
if (includeList != null) foreach (var filter in includeList) filter.entities.Remove(entityID);
if (excludeList != null) foreach (var filter in excludeList) filter.entities.Add(entityID);
}
#endregion
@ -288,23 +336,37 @@ namespace DCFApixels.DragonECS
public ent NewEntity()
{
int entityID = _entityDispenser.GetFree();
_entities.Add(entityID);
_entities.UncheckedAdd(entityID);
if (_gens.Length <= entityID)
{
Array.Resize(ref _gens, _gens.Length << 1);
return new ent(entityID, _gens[entityID]++, id);
_entities.OnWorldResize(_gens.Length);
foreach (var item in _groups)
{
item.OnWorldResize(_gens.Length);
}
foreach (var item in _pools)
{
item.OnWorldResize(_gens.Length);
}
}
ent entity = new ent(entityID, _gens[entityID]++, id);
_entityCreate.OnEntityCreate(entity);
return entity;
}
public void DelEntity(ent entity)
{
_entityDispenser.Release(entity.id);
_entities.UncheckedRemove(entity.id);
_entityDestry.OnEntityDestroy(entity);
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public ent GetEntity(int entityID)
{
if (_entities.Contains(entityID) == false)
return ent.NULL;
return new ent(entityID, _gens[entityID], id);
}
public void DelEntity(int entityID)
{
_entityDispenser.Release(entityID);
_entities.Remove(entityID);
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public bool EntityIsAlive(int entityID, short gen)
{
@ -325,6 +387,11 @@ namespace DCFApixels.DragonECS
_filters = null;
Realeze();
}
public void DestryWithPipeline()
{
Destroy();
_pipeline.Destroy();
}
#endregion
#region Utils
@ -344,7 +411,7 @@ namespace DCFApixels.DragonECS
static ComponentType()
{
uniqueID = ComponentType.increment++;
#if DEBUG || !DRAGONECS_NO_SANITIZE_CHECKS
#if (DEBUG && !DISABLE_DRAGONECS_DEBUG) || !DRAGONECS_NO_SANITIZE_CHECKS
if (ComponentType.increment + 1 > ushort.MaxValue)
{
throw new EcsFrameworkException($"No more room for new component for this {typeof(TArchetype).FullName} IWorldArchetype");
@ -374,15 +441,27 @@ namespace DCFApixels.DragonECS
}
}
#endregion
}
#region Extensions
public static class IEcsWorldExtensions
#region Other
void IEcsWorld.RegisterGroup(EcsGroup group)
{
public static void DelEntity(this IEcsWorld self, ent entity)
{
self.DelEntity(entity.id);
}
_groups.Add(group);
}
#endregion
}
[StructLayout(LayoutKind.Sequential, Pack = 8, Size = 24)]
internal readonly struct PoolRunnres
{
public readonly IEcsComponentAdd add;
public readonly IEcsComponentWrite write;
public readonly IEcsComponentDel del;
public PoolRunnres(EcsPipeline pipeline)
{
add = pipeline.GetRunner<IEcsComponentAdd>();
write = pipeline.GetRunner<IEcsComponentWrite>();
del = pipeline.GetRunner<IEcsComponentDel>();
}
}
}

View File

@ -0,0 +1,32 @@
using System;
using System.Linq;
using System.Runtime.CompilerServices;
namespace DCFApixels.DragonECS
{
internal static class ComponentResetHandler
{
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static IEcsComponentReset<T> New<T>()
{
Type targetType = typeof(T);
if (targetType.GetInterfaces().Contains(typeof(IEcsComponentReset<>).MakeGenericType(targetType)))
{
return (IEcsComponentReset<T>)Activator.CreateInstance(typeof(ComponentResetHandler<>).MakeGenericType(targetType));
}
return (IEcsComponentReset<T>)Activator.CreateInstance(typeof(ComponentResetDummy<>).MakeGenericType(targetType));
}
}
internal sealed class ComponentResetDummy<T> : IEcsComponentReset<T>
{
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public void Reset(ref T component) => component = default;
}
internal sealed class ComponentResetHandler<T> : IEcsComponentReset<T>
where T : IEcsComponentReset<T>
{
private T _fakeInstnace = default;
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public void Reset(ref T component) => _fakeInstnace.Reset(ref component);
}
}

View File

@ -97,18 +97,18 @@ namespace DCFApixels.DragonECS
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static bool IsAlive(this ref ent self)
{
using (_IsAliveMarker.Auto())
{
//using (_IsAliveMarker.Auto())
//{
bool result = EcsWorld.Worlds[self.world].EntityIsAlive(self.id, self.gen);
if (!result) self = ent.NULL;
return result;
}
//}
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static bool IsNull(this in ent self)
{
using (_IsNullMarker.Auto())
//using (_IsNullMarker.Auto())
return self == ent.NULL;
}
@ -116,28 +116,28 @@ namespace DCFApixels.DragonECS
public static ref readonly T Read<T>(this in ent self)
where T : struct
{
using (_ReadMarker.Auto())
//using (_ReadMarker.Auto())
return ref EcsWorld.Worlds[self.world].GetPool<T>().Read(self.id);
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static ref T Write<T>(this in ent self)
where T : struct
{
using (_WriteMarker.Auto())
//using (_WriteMarker.Auto())
return ref EcsWorld.Worlds[self.world].GetPool<T>().Write(self.id);
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static bool Has<T>(this in ent self)
where T : struct
{
using (_HasMarker.Auto())
//using (_HasMarker.Auto())
return EcsWorld.Worlds[self.world].GetPool<T>().Has(self.id);
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static void Del<T>(this in ent self)
where T : struct
{
using (_DelMarker.Auto())
//using (_DelMarker.Auto())
EcsWorld.Worlds[self.world].GetPool<T>().Del(self.id);
}
}