update queries

This commit is contained in:
Mikhail 2023-04-24 16:48:18 +08:00
parent cb25943b55
commit 24849ab619
13 changed files with 583 additions and 188 deletions

View File

@ -31,10 +31,10 @@ namespace DCFApixels.DragonECS
public void PreInit(EcsPipeline pipeline)
{
#if DEBUG && !DISABLE_DEBUG
for (int i = 0; i < Targets.Length && Targets.Length <= _markers.Length; i++)
for (int i = 0; i < targets.Length && targets.Length <= _markers.Length; i++)
{
using (_markers[i].Auto())
Targets[i].PreInit(pipeline);
targets[i].PreInit(pipeline);
}
#else
foreach (var item in targets) item.PreInit(pipeline);
@ -44,10 +44,10 @@ namespace DCFApixels.DragonECS
#if DEBUG && !DISABLE_DEBUG
protected override void OnSetup()
{
_markers = new EcsProfilerMarker[Targets.Length];
for (int i = 0; i < Targets.Length; i++)
_markers = new EcsProfilerMarker[targets.Length];
for (int i = 0; i < targets.Length; i++)
{
_markers[i] = new EcsProfilerMarker(EcsDebug.RegisterMark($"EcsRunner.{Targets[i].GetType().Name}.{nameof(PreInit)}"));
_markers[i] = new EcsProfilerMarker(EcsDebug.RegisterMark($"EcsRunner.{targets[i].GetType().Name}.{nameof(PreInit)}"));
}
}
#endif
@ -61,10 +61,10 @@ namespace DCFApixels.DragonECS
public void Init(EcsPipeline pipeline)
{
#if DEBUG && !DISABLE_DEBUG
for (int i = 0; i < Targets.Length && Targets.Length <= _markers.Length; i++)
for (int i = 0; i < targets.Length && targets.Length <= _markers.Length; i++)
{
using (_markers[i].Auto())
Targets[i].Init(pipeline);
targets[i].Init(pipeline);
}
#else
foreach (var item in targets) item.Init(pipeline);
@ -74,10 +74,10 @@ namespace DCFApixels.DragonECS
#if DEBUG && !DISABLE_DEBUG
protected override void OnSetup()
{
_markers = new EcsProfilerMarker[Targets.Length];
for (int i = 0; i < Targets.Length; i++)
_markers = new EcsProfilerMarker[targets.Length];
for (int i = 0; i < targets.Length; i++)
{
_markers[i] = new EcsProfilerMarker(EcsDebug.RegisterMark($"EcsRunner.{Targets[i].GetType().Name}.{nameof(Init)}"));
_markers[i] = new EcsProfilerMarker(EcsDebug.RegisterMark($"EcsRunner.{targets[i].GetType().Name}.{nameof(Init)}"));
}
}
#endif
@ -91,10 +91,10 @@ namespace DCFApixels.DragonECS
public void Run(EcsPipeline pipeline)
{
#if DEBUG && !DISABLE_DEBUG
for (int i = 0; i < Targets.Length && Targets.Length <= _markers.Length; i++)
for (int i = 0; i < targets.Length && targets.Length <= _markers.Length; i++)
{
using (_markers[i].Auto())
Targets[i].Run(pipeline);
targets[i].Run(pipeline);
}
#else
@ -105,10 +105,10 @@ namespace DCFApixels.DragonECS
#if DEBUG && !DISABLE_DEBUG
protected override void OnSetup()
{
_markers = new EcsProfilerMarker[Targets.Length];
for (int i = 0; i < Targets.Length; i++)
_markers = new EcsProfilerMarker[targets.Length];
for (int i = 0; i < targets.Length; i++)
{
_markers[i] = new EcsProfilerMarker(EcsDebug.RegisterMark($"EcsRunner.{Targets[i].GetType().Name}.{nameof(Run)}"));
_markers[i] = new EcsProfilerMarker(EcsDebug.RegisterMark($"EcsRunner.{targets[i].GetType().Name}.{nameof(Run)}"));
}
}
#endif
@ -122,10 +122,10 @@ namespace DCFApixels.DragonECS
public void Destroy(EcsPipeline pipeline)
{
#if DEBUG && !DISABLE_DEBUG
for (int i = 0; i < Targets.Length && Targets.Length <= _markers.Length; i++)
for (int i = 0; i < targets.Length && targets.Length <= _markers.Length; i++)
{
using (_markers[i].Auto())
Targets[i].Destroy(pipeline);
targets[i].Destroy(pipeline);
}
#else
foreach (var item in targets) item.Destroy(pipeline);
@ -135,10 +135,10 @@ namespace DCFApixels.DragonECS
#if DEBUG && !DISABLE_DEBUG
protected override void OnSetup()
{
_markers = new EcsProfilerMarker[Targets.Length];
for (int i = 0; i < Targets.Length; i++)
_markers = new EcsProfilerMarker[targets.Length];
for (int i = 0; i < targets.Length; i++)
{
_markers[i] = new EcsProfilerMarker(EcsDebug.RegisterMark($"EcsRunner.{Targets[i].GetType().Name}.{nameof(Destroy)}"));
_markers[i] = new EcsProfilerMarker(EcsDebug.RegisterMark($"EcsRunner.{targets[i].GetType().Name}.{nameof(Destroy)}"));
}
}
#endif

View File

@ -4,6 +4,11 @@ using System.Linq;
namespace DCFApixels.DragonECS
{
//TODO развить идею инжектов
//1) добавить расширенный метод инжекта, с 2 джинерик-аргументами, первый базовый тип и второй инжектируемый тип.
//напримере это будет работать так Inject<object, Foo> делает инжект объекта типа Foo для систем с IEcsInject<object> или с IEcsInject<Foo>
//2) добавить контейнер, который автоматически создается, собирает в себя все пре-инжекты и авто-инжектится во все системы.
//но это спорная идея
namespace Internal
{
internal class PreInitInjectController

29
src/Builtin/Queries.cs Normal file
View File

@ -0,0 +1,29 @@
namespace DCFApixels.DragonECS
{
public sealed class EmptyQuery : EcsQueryBase
{
private long _whereVersion;
public override long WhereVersion => _whereVersion;
public EmptyQuery(Builder b) { }
public sealed override WhereResult Where()
{
groupFilter = source.Entities.GetGroupInternal();
return new WhereResult(this, ++_whereVersion);
}
protected sealed override void OnBuild(Builder b) { }
}
public static partial class EcsWorldExtensions
{
public static WhereResult WhereAll(this EcsWorld self) => self.Select<EmptyQuery>().Where();
}
public sealed class HierarchyQuery : EcsJoinAttachQuery<Parent>
{
public HierarchyQuery(Builder b) { }
}
}

View File

@ -46,7 +46,7 @@ namespace DCFApixels.DragonECS
#region Methods
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public bool Contains(int entityID) => _source.Contains(entityID);
public bool Contains(int entityID) => _source.Has(entityID);
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public EcsGroup.Enumerator GetEnumerator() => _source.GetEnumerator();
[MethodImpl(MethodImplOptions.AggressiveInlining)]
@ -152,16 +152,17 @@ namespace DCFApixels.DragonECS
//защита от криворукости
//перед сборкой мусора снова создает сильную ссылку и возвращает в пул
//TODO переделат ьиил удалить, так как сборщик мусора просыпается только после 12к и более экземпляров, только тогда и вызывается финализатор, слишком жирно
//TODO переделат или удалить, так как сборщик мусора просыпается только после 12к и более экземпляров, только тогда и вызывается финализатор, слишком жирно
~EcsGroup()
{
Release();
}
#endregion
#region Contains
#region Has
//TODO переименовать в Has
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public bool Contains(int entityID)
public bool Has(int entityID)
{
return _sparse[entityID] > 0;
}
@ -179,7 +180,7 @@ namespace DCFApixels.DragonECS
public void UncheckedAdd(int entityID) => AddInternal(entityID);
public void Add(int entityID)
{
if (Contains(entityID)) return;
if (Has(entityID)) return;
AddInternal(entityID);
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
@ -204,7 +205,7 @@ namespace DCFApixels.DragonECS
public void UncheckedRemove(int entityID) => RemoveInternal(entityID);
public void Remove(int entityID)
{
if (!Contains(entityID)) return;
if (!Has(entityID)) return;
RemoveInternal(entityID);
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
@ -234,6 +235,15 @@ namespace DCFApixels.DragonECS
}
_delayedOps[_delayedOpsCount++] = entityID | isAddBitFlag; // delayedOp = entityID add isAddBitFlag
}
public void RemoveUnusedEntityIDs()
{
foreach (var e in this)
{
if (!_source.IsUsed(e))
AggressiveRemove(e);
}
}
#endregion
#region Sort/Clear
@ -290,7 +300,7 @@ namespace DCFApixels.DragonECS
if (_source != group.World) throw new ArgumentException("WorldIndex != groupFilter.WorldIndex");
#endif
foreach (var item in group)
if (!Contains(item))
if (!Has(item))
AggressiveAdd(item);
}
@ -304,7 +314,7 @@ namespace DCFApixels.DragonECS
if (_source != group.World) throw new ArgumentException("WorldIndex != groupFilter.WorldIndex");
#endif
foreach (var item in this)
if (group.Contains(item))
if (group.Has(item))
AggressiveRemove(item);
}
@ -318,7 +328,7 @@ namespace DCFApixels.DragonECS
if (World != group.World) throw new ArgumentException("WorldIndex != groupFilter.WorldIndex");
#endif
foreach (var item in this)
if (!group.Contains(item))
if (!group.Has(item))
AggressiveRemove(item);
}
@ -332,7 +342,7 @@ namespace DCFApixels.DragonECS
if (_source != group.World) throw new ArgumentException("WorldIndex != groupFilter.WorldIndex");
#endif
foreach (var item in group)
if (Contains(item))
if (Has(item))
AggressiveRemove(item);
else
AggressiveAdd(item);
@ -349,7 +359,7 @@ namespace DCFApixels.DragonECS
#endif
EcsGroup result = a._source.GetGroupFromPool();
foreach (var item in a)
if (!b.Contains(item))
if (!b.Has(item))
result.AggressiveAdd(item);
a._source.ReleaseGroup(a);
return result;
@ -363,7 +373,7 @@ namespace DCFApixels.DragonECS
#endif
EcsGroup result = a._source.GetGroupFromPool();
foreach (var item in a)
if (b.Contains(item))
if (b.Has(item))
result.AggressiveAdd(item);
a._source.ReleaseGroup(a);
return result;
@ -456,7 +466,7 @@ namespace DCFApixels.DragonECS
if (other.Count != Count)
return false;
foreach (var item in other)
if (!Contains(item))
if (!Has(item))
return false;
return true;
}

View File

@ -1,13 +1,17 @@
using Unity.Profiling;
using System;
using System.Xml.Schema;
using Unity.Profiling;
using UnityEditor.Search;
using UnityEngine;
namespace DCFApixels.DragonECS
{
public abstract class EcsJoinQueryBase : EcsQueryBase
public abstract class EcsJoinAttachQueryBase : EcsQueryBase
{
public abstract void ExecuteJoin();
public abstract void Join(WhereResult targetWorldWhereQuery);
}
public abstract class EcsJoinAttachQuery<TAttachComponent> : EcsJoinQueryBase
where TAttachComponent : struct, IEcsAttachComponent
public abstract class EcsJoinAttachQuery<TAttachComponent> : EcsJoinAttachQueryBase
where TAttachComponent : struct, IEcsAttachComponent
{
private EcsWorld _targetWorld;
private EcsAttachPool<TAttachComponent> _targetPool;
@ -19,39 +23,49 @@ namespace DCFApixels.DragonECS
private int[] _counts;
private EntityLinkedList _linkedBasket;
private bool _isJoinExecuted = false;
private bool _isInitTargetWorld = false;
private bool _isInitTargetWorlds = false;
private long _executeWhereVersion = 0;
private long _executeJoinVersion = 0;
private ProfilerMarker _execute = new ProfilerMarker("Query.ExecuteJoin");
private ProfilerMarker _executeWhere = new ProfilerMarker("JoinAttachQuery.Where");
private ProfilerMarker _executeJoin = new ProfilerMarker("JoinAttachQuery.Join");
#region Properties
public EcsWorld AttachWorld => _targetWorld;
public EcsAttachPool<TAttachComponent> Attach => _targetPool;
public bool IsJoinExecuted => _isJoinExecuted;
public sealed override long WhereVersion => _executeWhereVersion;
public long JoinVersion => _executeJoinVersion;
#endregion
protected sealed override void OnBuild(Builder b)
{
_targetPool = b.Include<TAttachComponent>();
}
public sealed override void ExecuteWhere()
public sealed override WhereResult Where()
{
//ExecuteWhere(_targetPool.Entities, groupFilter);
ExecuteWhere(World.Entities, groupFilter);
using (_executeWhere.Auto())
{
_executeWhereVersion++;
ExecuteWhere(_targetPool.Entities, groupFilter);
return new WhereResult(this, WhereVersion);
}
}
public sealed override void ExecuteJoin()
public sealed override void Join(WhereResult targetWorldWhereQuery)
{
_execute.Begin();
_isJoinExecuted = false;
if (_isInitTargetWorlds == false)
{
InitTargetWorlds();
if (_isInitTargetWorlds == false)
return;
}
_executeJoin.Begin();
#if (DEBUG && !DISABLE_DEBUG) || !DRAGONECS_NO_SANITIZE_CHECKS
if (targetWorldWhereQuery.IsNull)
throw new ArgumentNullException();//TODO составить текст исключения.
#endif
if (!_isInitTargetWorld)
InitTargetWorlds(targetWorldWhereQuery.World);
#if (DEBUG && !DISABLE_DEBUG) || !DRAGONECS_NO_SANITIZE_CHECKS
else
if (_targetWorld != targetWorldWhereQuery.World) throw new ArgumentException();//TODO составить текст исключения. это проверка на то что пользователь использует правильный мир
#endif
//Подготовка массивов
if (_targetWorldCapacity < _targetWorld.Capacity)
@ -73,19 +87,137 @@ namespace DCFApixels.DragonECS
_linkedBasket.Clear();
//Конец подготовки массивов
ExecuteWhere();
Where();
foreach (var attachID in groupFilter)
{
EcsEntity attachTarget = _targetPool.Read(attachID).Target;
if (!attachTarget.IsAlive)//TODO пофиксить IsAlive
if (!attachTarget.IsAlive)
{
//_targetPool.Del(attachID);
continue;
}
int attachTargetID = attachTarget.id;
ref int nodeIndex = ref _mapping[attachTargetID];
if(nodeIndex <= 0)
ref int nodeIndex = ref _mapping[attachTargetID];
if (nodeIndex <= 0)
nodeIndex = _linkedBasket.Add(attachID);
else
_linkedBasket.Insert(nodeIndex, attachID);
_counts[attachTargetID]++;
}
_executeJoinVersion++;
_executeJoin.End();
}
private void InitTargetWorlds(EcsWorld targetWorld)
{
_targetWorld = targetWorld;
_targetWorldCapacity = _targetWorld.Capacity;
_mapping = new int[_targetWorldCapacity];
_counts = new int[_targetWorldCapacity];
_targetPoolCapacity = _targetPool.Capacity;
_linkedBasket = new EntityLinkedList(_targetPoolCapacity);
_isInitTargetWorld = true;
}
public bool Has(int attachedEnttiyID) => groupFilter.Has(attachedEnttiyID);
public EntityLinkedList.EnumerableSpan GetNodes(int entityID) => _linkedBasket.Span(_mapping[entityID], _counts[entityID]);
public int GetNode(int entityID) => _counts[entityID] > 0 ? _linkedBasket.Get(_mapping[entityID]) : 0;
public int GetNodesCount(int entityID) => _counts[entityID];
public bool HasNode(int entityID, int attachedEntityID) => groupFilter.Has(attachedEntityID) && _targetPool.Read(attachedEntityID).Target.id == entityID;
}
/*
public abstract class EcsJoinRelationQuery<TRelationComponent> : EcsQueryBase
where TRelationComponent : struct, IEcsRelationComponent
{
private EcsWorld _firstWorld;
private EcsWorld _secondWorld;
private EcsRelationPool<TRelationComponent> _targetPool;
internal int _targetPoolCapacity = -1;
private bool _isInitTargetWorlds = false;
private bool _isJoinExecuted = false;
private long _executeWhereVersion = 0;
private long _executeJoinVersion = 0;
public readonly Orientation ToSecond = new Orientation();
public readonly Orientation ToFirst = new Orientation();
private ProfilerMarker _executeWhere = new ProfilerMarker("JoinRelationQuery.Where");
private ProfilerMarker _executeJoin = new ProfilerMarker("JoinRelationQuery.Join");
#region Properties
public EcsWorld RelationFirstWorld => _firstWorld;
public EcsWorld RelationSecondWorld => _secondWorld;
public EcsRelationPool<TRelationComponent> Relation => _targetPool;
public bool IsMonoWorldRelation => _firstWorld == _secondWorld;
public sealed override long WhereVersion => _executeWhereVersion;
public long JoinVersion => _executeJoinVersion;
#endregion
protected sealed override void OnBuild(Builder b)
{
_targetPool = b.Include<TRelationComponent>();
}
public sealed override WhereResult Where()
{
using (_executeWhere.Auto())
{
_executeWhereVersion++;
ExecuteWhere(_targetPool.Entities, groupFilter);
return new WhereResult(this, WhereVersion);
}
}
public void Join(WhereResult firstWorldWhereQuery, WhereResult secondWorldWhereQuery)
{
_executeJoin.Begin();
_isJoinExecuted = false;
if (_isInitTargetWorlds == false)
{
InitTargetWorlds();
if (_isInitTargetWorlds == false)
return;
};
// //Подготовка массивов
// if (_targetWorldCapacity < _targetWorld.Capacity)
// {
// _targetWorldCapacity = _targetWorld.Capacity;
// _mapping = new int[_targetWorldCapacity];
// _counts = new int[_targetWorldCapacity];
// }
// else
// {
// ArrayUtility.Fill(_counts, 0);
// ArrayUtility.Fill(_mapping, 0);
// }
// if (_targetPoolCapacity < _targetPool.Capacity)
// {
// _targetPoolCapacity = _targetPool.Capacity;
// _linkedBasket.Resize(_targetPoolCapacity);
// }
// _linkedBasket.Clear();
// //Конец подготовки массивов
Where();
foreach (var attachID in groupFilter)
{
EcsEntity attachTarget = _targetPool.Read(attachID).First;
if (!attachTarget.IsAlive)
{
//_targetPool.Del(attachID);
continue;
}
int attachTargetID = attachTarget.id;
ref int nodeIndex = ref _mapping[attachTargetID];
if (nodeIndex <= 0)
nodeIndex = _linkedBasket.Add(attachID);
else
_linkedBasket.Insert(nodeIndex, attachID);
@ -93,66 +225,12 @@ namespace DCFApixels.DragonECS
}
_isJoinExecuted = true;
_execute.End();
}
private void InitTargetWorlds()
{
foreach (var e in _targetPool.Entities)
{
ref readonly var rel = ref _targetPool.Read(e);
//if (rel.Target.IsNotNull)
_targetWorld = EcsWorld.Worlds[rel.Target.world];
_executeJoinVersion++;
_executeJoin.End();
if (_targetWorld != null)
{
_isInitTargetWorlds = true;
break;
}
}
if (_isInitTargetWorlds)
{
_targetWorldCapacity = _targetWorld.Capacity;
_mapping = new int[_targetWorldCapacity];
_counts = new int[_targetWorldCapacity];
_targetPoolCapacity = _targetPool.Capacity;
//_entites = new int[_targetPoolCapacity];
_linkedBasket = new EntityLinkedList(_targetPoolCapacity);
}
}
public EcsGroup.Enumerator GetEnumerator()
{
return groupFilter.GetEnumerator();
}
public EntityLinkedList.EnumerableSpan GetNodes(int entityID) => _linkedBasket.Span(_mapping[entityID], _counts[entityID]);
}
public abstract class EcsJoinRelationQuery<TRelationComponent> : EcsJoinQueryBase
where TRelationComponent : struct, IEcsRelationComponent
{
private EcsWorld _firstWorld;
private EcsWorld _secondWorld;
private EcsRelationPool<TRelationComponent> _targetPool;
private bool _isInitTargetWorlds = false;
#region Properties
public EcsWorld RelationFirstWorld => _firstWorld;
public EcsWorld RelationSecondWorld => _secondWorld;
public EcsRelationPool<TRelationComponent> Relation => _targetPool;
public bool IsMonoWorldRelation => _firstWorld == _secondWorld;
#endregion
protected sealed override void OnBuild(Builder b)
{
_targetPool = b.Include<TRelationComponent>();
}
public sealed override void ExecuteWhere()
{
ExecuteWhere(_targetPool.Entites, groupFilter);
}
public sealed override void ExecuteJoin()
{
if (_isInitTargetWorlds == false) InitTargetWorlds();
_executeJoinVersion++;
_isJoinExecuted = true;
_executeJoin.End();
}
private void InitTargetWorlds()
@ -170,10 +248,78 @@ namespace DCFApixels.DragonECS
break;
}
}
if (_isInitTargetWorlds)
{
_targetWorldCapacity = _targetWorld.Capacity;
_mapping = new int[_targetWorldCapacity];
_counts = new int[_targetWorldCapacity];
_targetPoolCapacity = _targetPool.Capacity;
_linkedBasket = new EntityLinkedList(_targetPoolCapacity);
}
}
public EcsGroup.Enumerator GetEnumerator()
public bool Has(int relationEntityID) => groupFilter.Has(relationEntityID);
public class Orientation
{
return groupFilter.GetEnumerator();
internal int targetWorldCapacity = -1;
internal int[] mapping;
internal int[] counts;
internal EntityLinkedList linkedBasket;
public bool HasRelation(int fromEntityID, int toEntityID)
{
throw new NotImplementedException();
}
public bool HasNode(int fromEntityID, int toEntityID)
{
throw new NotImplementedException();
}
public EntityLinkedList.EnumerableSpan GetRelations(int fromEntityID) => linkedBasket.Span(mapping[fromEntityID], counts[fromEntityID]);
public int GetRelation(int fromEntityID) => counts[fromEntityID] > 0 ? linkedBasket.Get(mapping[fromEntityID]) : 0;
public int GetRelationsCount(int fromEntityID) => counts[fromEntityID];
}
}*/
#region Extensions
public static class EcsJoinQueryBaseExtensions
{
public static void Join(this EcsJoinAttachQueryBase self, EcsWorld targetWorld)
{
self.Join(targetWorld.Where<EmptyQuery>());
}
public static TQuery Join<TQuery>(this EcsWorld self, EcsWorld targetWorld, out TQuery query) where TQuery : EcsJoinAttachQueryBase
{
return self.Join(targetWorld.WhereAll(), out query);
}
public static TQuery Join<TQuery>(this EcsWorld self, EcsWorld targetWorld) where TQuery : EcsJoinAttachQueryBase
{
return self.Join<TQuery>(targetWorld.WhereAll());
}
/* public static class EcsJoinRelationQueryExtensions
{
public static void Join<TRelationComponent>(this EcsJoinRelationQuery<TRelationComponent> self, EcsWorld firstWorld, EcsWorld secondWorld)
where TRelationComponent : struct, IEcsRelationComponent
{
self.Join(firstWorld.Where<EmptyQuery>(), secondWorld.Where<EmptyQuery>());
}
public static void Join<TRelationComponent>(this EcsJoinRelationQuery<TRelationComponent> self, EcsWorld firstWorld, WhereResult secondWorldWhereQuery)
where TRelationComponent : struct, IEcsRelationComponent
{
self.Join(firstWorld.Where<EmptyQuery>(), secondWorldWhereQuery);
}
public static void Join<TRelationComponent>(this EcsJoinRelationQuery<TRelationComponent> self, WhereResult firstWorldWhereQuery, EcsWorld secondWorld)
where TRelationComponent : struct, IEcsRelationComponent
{
self.Join(firstWorldWhereQuery, secondWorld.Where<EmptyQuery>());
}
}*/
}
#endregion
}

View File

@ -1,6 +1,9 @@
using System;
using System.Collections.Generic;
using System.Drawing;
using System.Reflection;
using System.Runtime.CompilerServices;
using System.Runtime.InteropServices;
using Unity.Profiling;
namespace DCFApixels.DragonECS
@ -11,15 +14,19 @@ namespace DCFApixels.DragonECS
internal EcsGroup groupFilter;
internal EcsQueryMask mask;
private bool _isInit;
#region Properties
internal EcsQueryMask Mask => mask;
public EcsQueryMask Mask => mask;
public EcsWorld World => source;
public bool IsInit => _isInit;
public abstract long WhereVersion { get; }
#endregion
#region Builder
protected virtual void Init(Builder b) { }
protected abstract void OnBuild(Builder b);
public abstract void ExecuteWhere();
public sealed class Builder : EcsQueryBuilderBase
{
private EcsWorld _world;
@ -53,6 +60,7 @@ namespace DCFApixels.DragonECS
newQuery.source = world;
newQuery.OnBuild(builder);
builder.End(out newQuery.mask);
newQuery._isInit = true;
return (TQuery)(object)newQuery;
}
@ -82,10 +90,11 @@ namespace DCFApixels.DragonECS
}
}
#endregion
public abstract WhereResult Where();
protected void ExecuteWhere(EcsReadonlyGroup group, EcsGroup result)
{
var pools = World.Pools;
var pools = World.pools;
result.Clear();
foreach (var e in group)
{
@ -103,6 +112,7 @@ namespace DCFApixels.DragonECS
next: continue;
}
}
//protected void IsMaskCompatible
protected void ExecuteWhereAndSort(EcsReadonlyGroup group, EcsGroup result)
{
ExecuteWhere(group, result);
@ -110,20 +120,38 @@ namespace DCFApixels.DragonECS
}
}
//TODO есть идея проверки того что запросбылвыполнен путем создания ref struct которыйсодержит результат выполнения заапроса и существует только в стеке.
//таким образом для каждой системы он будет выполняться единожды, без холостых перезапусков, скорее всего эта система будет работать лучше чем "Версии запросов"
public abstract class EcsQuery : EcsQueryBase
{
private ProfilerMarker _execute = new ProfilerMarker("EcsQuery.ExecuteWhere");
private ProfilerMarker _execute = new ProfilerMarker("EcsQuery.Where");
private long _executeWhereVersion = 0;
#region Properties
//на данный момент бесполное свойство, основная идея, реализовать все методы запроса с аргументом (..., ref long version),
//далее в теле метододов сравнивать с текущей версией, и если они отличаются, выполнять запрос и перезаписывать значение version
//таким образом можно добиться сокращение "холостых" выполнений запроса, тоесть в рамках одной системы запрос всегда будет выполняться один раз
//даже при повторном вызове.
//Но нужно добавить метод для принудительного повторения запроса без сравнения вресий.
//TODO реализовать описанное выше поведение
//TODO проверить что лучше подходит int или long. long делает этот механизм очень надежным, но возможно его использование будет существенно влиять на производительность.
public sealed override long WhereVersion => _executeWhereVersion;
#endregion
protected sealed override void OnBuild(Builder b) { }
public sealed override void ExecuteWhere()
public sealed override WhereResult Where()
{
using (_execute.Auto())
{
ExecuteWhereAndSort(World.Entities, groupFilter);
return new WhereResult(this, ++_executeWhereVersion);
}
}
public EcsGroup.Enumerator GetEnumerator()
public bool Has(int entityID)
{
return groupFilter.GetEnumerator();
return groupFilter.Has(entityID);
}
}
@ -142,4 +170,26 @@ namespace DCFApixels.DragonECS
public abstract TPool Exclude<TComponent, TPool>() where TComponent : struct where TPool : EcsPoolBase<TComponent>, new();
public abstract TPool Optional<TComponent, TPool>() where TComponent : struct where TPool : EcsPoolBase<TComponent>, new();
}
[StructLayout(LayoutKind.Sequential, Pack = 8, Size = 16)]
public readonly ref struct WhereResult
{
public readonly EcsQueryBase query; //ref = 8 byte
public readonly long version; //long = 8 byte
#region Properties
public bool IsNull => query == null;
public EcsWorld World => query.World;
public bool IsActual => query.WhereVersion == version;
#endregion
public WhereResult(EcsQueryBase query, long version)
{
this.query = query;
this.version = version;
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public EcsGroup.Enumerator GetEnumerator() => query.groupFilter.GetEnumerator();
}
}

View File

@ -9,12 +9,12 @@ namespace DCFApixels.DragonECS
{
private const short GEN_BITS = 0x7fff;
private const short DEATH_GEN_BIT = short.MinValue;
private const int DEL_ENT_BUFFER_SIZE_OFFSET = 2;
public static EcsWorld[] Worlds = new EcsWorld[8];
private static IntDispenser _worldIdDispenser = new IntDispenser(0);
public readonly short uniqueID;
private const int DEL_ENT_BUFFER_SIZE_OFFSET = 2;
private int _worldArchetypeID;
private IntDispenser _entityDispenser;
@ -30,7 +30,7 @@ namespace DCFApixels.DragonECS
private int[] _delEntBuffer;
private int _delEntBufferCount;
private EcsPoolBase[] _pools;
internal EcsPoolBase[] pools;
private EcsNullPool _nullPool;
private EcsQueryBase[] _queries;
@ -43,12 +43,6 @@ namespace DCFApixels.DragonECS
private IEcsEntityCreate _entityCreate;
private IEcsEntityDestroy _entityDestry;
#region GetterMethods
public ReadOnlySpan<EcsPoolBase> GetAllPools() => new ReadOnlySpan<EcsPoolBase>(_pools);
public int GetComponentID<T>() => WorldMetaStorage.GetComponentId<T>(_worldArchetypeID);////ComponentType<TWorldArchetype>.uniqueID;
#endregion
#region Properties
public abstract Type Archetype { get; }
public int UniqueID => uniqueID;
@ -56,14 +50,7 @@ namespace DCFApixels.DragonECS
public int Capacity => _entitesCapacity; //_denseEntities.Length;
public EcsPipeline Pipeline => _pipeline;
public EcsReadonlyGroup Entities => _allEntites.Readonly;
#endregion
#region Internal Properties
internal EcsPoolBase[] Pools
{
[MethodImpl(MethodImplOptions.AggressiveInlining)]
get => _pools;
}
public ReadOnlySpan<EcsPoolBase> AllPools => pools;
#endregion
#region Constructors/Destroy
@ -82,8 +69,8 @@ namespace DCFApixels.DragonECS
if (!_pipeline.IsInit) pipline.Init();
_entityDispenser = new IntDispenser(0);
_nullPool = EcsNullPool.instance;
_pools = new EcsPoolBase[512];
ArrayUtility.Fill(_pools, _nullPool);
pools = new EcsPoolBase[512];
ArrayUtility.Fill(pools, _nullPool);
_gens = new short[_entitesCapacity];
ArrayUtility.Fill(_gens, DEATH_GEN_BIT);
@ -105,7 +92,7 @@ namespace DCFApixels.DragonECS
_entityDispenser = null;
//_denseEntities = null;
_gens = null;
_pools = null;
pools = null;
_nullPool = null;
_queries = null;
@ -119,38 +106,37 @@ namespace DCFApixels.DragonECS
}
#endregion
#region GetComponentID
public int GetComponentID<T>() => WorldMetaStorage.GetComponentId<T>(_worldArchetypeID);////ComponentType<TWorldArchetype>.uniqueID;
#endregion
#region GetPool
public TPool GetPool<TComponent, TPool>() where TComponent : struct where TPool : EcsPoolBase<TComponent>, new()
{
int uniqueID = WorldMetaStorage.GetComponentId<TComponent>(_worldArchetypeID);
if (uniqueID >= _pools.Length)
if (uniqueID >= pools.Length)
{
int oldCapacity = _pools.Length;
Array.Resize(ref _pools, _pools.Length << 1);
ArrayUtility.Fill(_pools, _nullPool, oldCapacity, oldCapacity - _pools.Length);
int oldCapacity = pools.Length;
Array.Resize(ref pools, pools.Length << 1);
ArrayUtility.Fill(pools, _nullPool, oldCapacity, oldCapacity - pools.Length);
}
if (_pools[uniqueID] == _nullPool)
if (pools[uniqueID] == _nullPool)
{
var pool = new TPool();
_pools[uniqueID] = pool;
pools[uniqueID] = pool;
pool.InvokeInit(this);
//EcsDebug.Print(pool.GetType().FullName);
}
return (TPool)_pools[uniqueID];
return (TPool)pools[uniqueID];
}
#endregion
#region Queries
public TQuery Where<TQuery>(out TQuery query) where TQuery : EcsQuery
{
query = Select<TQuery>();
query.ExecuteWhere();
return query;
}
public TQuery Select<TQuery>() where TQuery : EcsQueryBase
{
int uniqueID = WorldMetaStorage.GetQueryId<TQuery>(_worldArchetypeID);
@ -160,6 +146,28 @@ namespace DCFApixels.DragonECS
_queries[uniqueID] = EcsQueryBase.Builder.Build<TQuery>(this);
return (TQuery)_queries[uniqueID];
}
public WhereResult Where<TQuery>(out TQuery query) where TQuery : EcsQueryBase
{
query = Select<TQuery>();
return query.Where();
}
public WhereResult Where<TQuery>() where TQuery : EcsQueryBase
{
return Select<TQuery>().Where();
}
public TQuery Join<TQuery>(WhereResult targetWorldWhereQuery, out TQuery query) where TQuery : EcsJoinAttachQueryBase
{
query = Select<TQuery>();
query.Join(targetWorldWhereQuery);
return query;
}
public TQuery Join<TQuery>(WhereResult targetWorldWhereQuery) where TQuery : EcsJoinAttachQueryBase
{
TQuery query = Select<TQuery>();
query.Join(targetWorldWhereQuery);
return query;
}
#endregion
#region IsMaskCompatible
@ -171,12 +179,12 @@ namespace DCFApixels.DragonECS
#endif
for (int i = 0, iMax = mask.Inc.Length; i < iMax; i++)
{
if (!_pools[mask.Inc[i]].Has(entityID))
if (!pools[mask.Inc[i]].Has(entityID))
return false;
}
for (int i = 0, iMax = mask.Exc.Length; i < iMax; i++)
{
if (_pools[mask.Exc[i]].Has(entityID))
if (pools[mask.Exc[i]].Has(entityID))
return false;
}
return true;
@ -208,12 +216,11 @@ namespace DCFApixels.DragonECS
_groups.RemoveAt(last);
}
}
foreach (var item in _pools)
foreach (var item in pools)
item.InvokeOnWorldResize(_gens.Length);
}
_gens[entityID] &= GEN_BITS;
EcsEntity entity = new EcsEntity(entityID, ++_gens[entityID], uniqueID);
// UnityEngine.Debug.Log($"{entityID} {_gens[entityID]} {uniqueID}");
_entityCreate.OnEntityCreate(entity);
_allEntites.Add(entityID);
return entity;
@ -227,26 +234,20 @@ namespace DCFApixels.DragonECS
_entityDestry.OnEntityDestroy(entity);
if (_delEntBufferCount >= _delEntBuffer.Length)
ReleaseDelEntBuffer();
ReleaseDelEntityBuffer();
}
private void ReleaseDelEntBuffer()//TODO проверить что буфер удаления работает нормально
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public EcsEntity GetEcsEntity(int entityID) => new EcsEntity(entityID, _gens[entityID], uniqueID); //TODO придумать получше имя метода
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public bool IsAlive(int entityID, short gen) => _gens[entityID] == gen;
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public bool IsUsed(int entityID) => (_gens[entityID] | DEATH_GEN_BIT) == 0;
public void ReleaseDelEntityBuffer()
{
for (int i = 0; i < _delEntBufferCount; i++)
_entityDispenser.Release(_delEntBuffer[i]);
_delEntBufferCount = 0;
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public EcsEntity GetEcsEntity(int entityID)
{
return new EcsEntity(entityID, _gens[entityID], uniqueID);
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public bool EntityIsAlive(int entityID, short gen) //TODO пофиксить EntityIsAlive
{
return _gens[entityID] == gen;
}
#endregion
#region Groups

View File

@ -8,7 +8,7 @@ namespace DCFApixels.DragonECS
// gen - 16 bits
// world - 16 bits
/// <summary>Strong identifier/Permanent entity identifier</summary>
[StructLayout(LayoutKind.Explicit, Pack =2, Size = 8)]
[StructLayout(LayoutKind.Explicit, Pack = 2, Size = 8)]
public readonly partial struct EcsEntity : IEquatable<long>, IEquatable<EcsEntity>
{
public static readonly EcsEntity NULL = default;
@ -22,9 +22,9 @@ namespace DCFApixels.DragonECS
public readonly short world;
//[MethodImpl(MethodImplOptions.AggressiveInlining)]
//public ent ToEnt() => EcsWorld.Worlds[world].EntityIsAlive(id, gen) ? new ent(id) : default;
//public ent ToEnt() => EcsWorld.Worlds[world].IsAlive(id, gen) ? new ent(id) : default;
public bool IsAlive => EcsWorld.Worlds[world].EntityIsAlive(id, gen);
public bool IsAlive => EcsWorld.Worlds[world].IsAlive(id, gen);
#region Constructors
[MethodImpl(MethodImplOptions.AggressiveInlining)]
@ -104,7 +104,7 @@ namespace DCFApixels.DragonECS
// {
// //using (_IsAliveMarker.Auto())
// //{
// bool result = EcsWorld.Worlds[self.world].EntityIsAlive(self.id, self.gen);
// bool result = EcsWorld.Worlds[self.world].IsAlive(self.id, self.gen);
// if (!result) self = EcsEntity.NULL;
// return result;
// //}

View File

@ -15,7 +15,14 @@ namespace DCFApixels.DragonECS
private PoolRunners _poolRunners;
private EcsGroup _entities;
public EcsReadonlyGroup Entities => _entities.Readonly;
public EcsReadonlyGroup Entities
{
get
{
_entities.RemoveUnusedEntityIDs();
return _entities.Readonly;
}
}
#if (DEBUG && !DISABLE_DEBUG) || !DRAGONECS_NO_SANITIZE_CHECKS
private short _sanitizeTargetWorld = -1;

View File

@ -0,0 +1,143 @@
using System;
using System.Collections;
using System.Collections.Generic;
using System.Runtime.CompilerServices;
using Unity.Profiling;
namespace DCFApixels.DragonECS
{
public sealed class EcsReadonlyPool<T> : EcsPoolBase<T>
where T : struct, IEcsReadonlyComponent
{
public static string name = typeof(T).Name;
private EcsWorld _source;
private int[] _mapping;// index = entityID / value = itemIndex;/ value = 0 = no entityID
private T[] _items; //dense
private int _itemsCount;
private int[] _recycledItems;
private int _recycledItemsCount;
private IEcsComponentReset<T> _componentResetHandler;
private PoolRunners _poolRunners;
#region Properites
public int Count => _itemsCount;
public int Capacity => _items.Length;
public sealed override EcsWorld World => _source;
#endregion
#region Init
protected override void Init(EcsWorld world)
{
const int capacity = 512;
_source = world;
_mapping = new int[world.Capacity];
_recycledItems = new int[128];
_recycledItemsCount = 0;
_items = new T[capacity];
_itemsCount = 0;
_componentResetHandler = EcsComponentResetHandler<T>.instance;
_poolRunners = new PoolRunners(world.Pipeline);
}
#endregion
#region Write/Read/Has/Del
private ProfilerMarker _addMark = new ProfilerMarker("EcsPoo.Add");
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 Add(int entityID)
{
// using (_addMark.Auto())
// {
ref int itemIndex = ref _mapping[entityID];
if (itemIndex <= 0)
{
if (_recycledItemsCount > 0)
{
itemIndex = _recycledItems[--_recycledItemsCount];
_itemsCount++;
}
else
{
itemIndex = ++_itemsCount;
if (itemIndex >= _items.Length)
Array.Resize(ref _items, _items.Length << 1);
}
//_mapping[entityID] = itemIndex; TODO проверить что это лишнее дейсвие
_poolRunners.add.OnComponentAdd<T>(entityID);
}
else
{
_componentResetHandler.Reset(ref _items[itemIndex]);
}
_poolRunners.write.OnComponentWrite<T>(entityID);
return ref _items[itemIndex];
// }
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public ref readonly T Read(int entityID)
{
// using (_readMark.Auto())
return ref _items[_mapping[entityID]];
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public sealed override bool Has(int entityID)
{
// using (_hasMark.Auto())
return _mapping[entityID] > 0;
}
public void Del(int entityID)
{
// using (_delMark.Auto())
// {
ref int itemIndex = ref _mapping[entityID];
_componentResetHandler.Reset(ref _items[itemIndex]);
if (_recycledItemsCount >= _recycledItems.Length)
Array.Resize(ref _recycledItems, _recycledItems.Length << 1);
_recycledItems[_recycledItemsCount++] = itemIndex;
itemIndex = 0;
_itemsCount--;
_poolRunners.del.OnComponentDel<T>(entityID);
// }
}
#endregion
#region WorldCallbacks
protected override void OnWorldResize(int newSize)
{
Array.Resize(ref _mapping, newSize);
}
protected override void OnDestroy() { }
#endregion
}
public interface IEcsReadonlyComponent { }
public static class EcsReadonlyPoolExt
{
public static EcsReadonlyPool<TReadolnyComponent> GetPool<TReadolnyComponent>(this EcsWorld self) where TReadolnyComponent : struct, IEcsReadonlyComponent
{
return self.GetPool<TReadolnyComponent, EcsReadonlyPool<TReadolnyComponent>>();
}
public static EcsReadonlyPool<TReadolnyComponent> Include<TReadolnyComponent>(this EcsQueryBuilderBase self) where TReadolnyComponent : struct, IEcsReadonlyComponent
{
return self.Include<TReadolnyComponent, EcsReadonlyPool<TReadolnyComponent>>();
}
public static EcsReadonlyPool<TReadolnyComponent> Exclude<TReadolnyComponent>(this EcsQueryBuilderBase self) where TReadolnyComponent : struct, IEcsReadonlyComponent
{
return self.Exclude<TReadolnyComponent, EcsReadonlyPool<TReadolnyComponent>>();
}
public static EcsReadonlyPool<TReadolnyComponent> Optional<TReadolnyComponent>(this EcsQueryBuilderBase self) where TReadolnyComponent : struct, IEcsReadonlyComponent
{
return self.Optional<TReadolnyComponent, EcsReadonlyPool<TReadolnyComponent>>();
}
}
}

View File

@ -15,7 +15,7 @@ namespace DCFApixels.DragonECS
private PoolRunners _poolRunners;
private EcsGroup _entities;
public EcsReadonlyGroup Entites => _entities.Readonly;
public EcsReadonlyGroup Entities => _entities.Readonly;
#if (DEBUG && !DISABLE_DEBUG) || !DRAGONECS_NO_SANITIZE_CHECKS
private short _sanitizeFirstWorld = -1;

View File

@ -21,6 +21,11 @@ namespace DCFApixels.DragonECS
private PoolRunners _poolRunners;
#region Properites
public ref T Instance
{
[MethodImpl(MethodImplOptions.AggressiveInlining)]
get => ref _component;
}
public int Count => _count;
public sealed override EcsWorld World => _source;
#endregion
@ -53,6 +58,7 @@ namespace DCFApixels.DragonECS
return ref _component;
// }
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public ref T Write(int entityID)
{
@ -91,7 +97,7 @@ namespace DCFApixels.DragonECS
protected override void OnDestroy() { }
#endregion
}
/// <summary> Singleton component </summary>
public interface IEcsSingleComponent { }
public static class EcsSinglePoolExt
{

View File

@ -39,10 +39,8 @@ namespace DCFApixels.DragonECS
_count = 0;
}
public void Set(int nodeIndex, int entityID)
{
_nodes[nodeIndex].entityID = entityID;
}
public void Set(int nodeIndex, int entityID) => _nodes[nodeIndex].entityID = entityID;
public int Get(int nodeIndex) => _nodes[nodeIndex].entityID;
/// <summary> Insert after</summary>
/// <returns> new node index</returns>