From 2e121381f7e13aace50427cc754cd23684222591 Mon Sep 17 00:00:00 2001 From: Mikhail <99481254+DCFApixels@users.noreply.github.com> Date: Thu, 31 Oct 2024 14:46:21 +0800 Subject: [PATCH] update masks --- src/EcsAspect.cs | 89 +++--- src/EcsMask.cs | 471 ++++------------------------- src/EcsPipeline.Builder.cs | 2 +- src/EcsPipeline.cs | 22 +- src/EcsStaticMask.cs | 419 +++++++++++++++++++++++++ src/Executors/EcsQueryExecutor.cs | 10 +- src/Internal/EcsTypeCodeManager.cs | 11 + 7 files changed, 548 insertions(+), 476 deletions(-) create mode 100644 src/EcsStaticMask.cs diff --git a/src/EcsAspect.cs b/src/EcsAspect.cs index da2043b..baf381b 100644 --- a/src/EcsAspect.cs +++ b/src/EcsAspect.cs @@ -1,7 +1,6 @@ using DCFApixels.DragonECS.Internal; using DCFApixels.DragonECS.PoolsCore; using System; -using System.Collections.Generic; namespace DCFApixels.DragonECS { @@ -9,62 +8,31 @@ namespace DCFApixels.DragonECS { #region Initialization Halpers [ThreadStatic] - private static Stack _constructorBuildersStack = null; - private static Stack GetBuildersStack() - { - if (_constructorBuildersStack == null) - { - _constructorBuildersStack = new Stack(); - } - return _constructorBuildersStack; - } + private static Builder[] _constructorBuildersStack; + [ThreadStatic] + private static int _constructorBuildersStackIndex; protected static Builder CurrentBuilder { get { - var buildersStack = GetBuildersStack(); - if (buildersStack.Count <= 0) + if (_constructorBuildersStack == null || _constructorBuildersStackIndex < 0) { Throw.Aspect_CanOnlyBeUsedDuringInitialization(nameof(CurrentBuilder)); } - return buildersStack.Peek(); + return _constructorBuildersStack[_constructorBuildersStackIndex]; } } protected static IncludeMarker Inc { - get - { - var buildersStack = GetBuildersStack(); - if (buildersStack.Count <= 0) - { - Throw.Aspect_CanOnlyBeUsedDuringInitialization(nameof(Inc)); - } - return buildersStack.Peek().Inc; - } + get { return CurrentBuilder.Inc; } } protected static ExcludeMarker Exc { - get - { - var buildersStack = GetBuildersStack(); - if (buildersStack.Count <= 0) - { - Throw.Aspect_CanOnlyBeUsedDuringInitialization(nameof(Exc)); - } - return buildersStack.Peek().Exc; - } + get { return CurrentBuilder.Exc; } } protected static OptionalMarker Opt { - get - { - var buildersStack = GetBuildersStack(); - if (buildersStack.Count <= 0) - { - Throw.Aspect_CanOnlyBeUsedDuringInitialization(nameof(Opt)); - } - return buildersStack.Peek().Opt; - } + get { return CurrentBuilder.Opt; } } #endregion @@ -99,7 +67,7 @@ namespace DCFApixels.DragonECS public sealed class Builder { private EcsWorld _world; - private EcsMask.Builder _maskBuilder; + private EcsStaticMask.Builder _maskBuilder; #region Properties public IncludeMarker Inc @@ -121,29 +89,42 @@ namespace DCFApixels.DragonECS #endregion #region Constructors/New - private Builder(EcsWorld world) + private Builder() { } + private void Reset(EcsWorld world) { + _maskBuilder = EcsStaticMask.New(); _world = world; - _maskBuilder = EcsMask.New(world); } internal static unsafe TAspect New(EcsWorld world) where TAspect : EcsAspect, new() { - Builder builder = new Builder(world); - Type aspectType = typeof(TAspect); - EcsAspect newAspect; + if (_constructorBuildersStack == null) + { + _constructorBuildersStack = new Builder[4]; + _constructorBuildersStackIndex = -1; + } - var buildersStack = GetBuildersStack(); + _constructorBuildersStackIndex++; + if (_constructorBuildersStackIndex >= _constructorBuildersStack.Length) + { + Array.Resize(ref _constructorBuildersStack, _constructorBuildersStack.Length << 1); + } + Builder builder = _constructorBuildersStack[_constructorBuildersStackIndex]; + if (builder == null) + { + builder = new Builder(); + _constructorBuildersStack[_constructorBuildersStackIndex] = builder; + } + builder.Reset(world); - buildersStack.Push(builder); - newAspect = new TAspect(); + TAspect newAspect = new TAspect(); newAspect.Init(builder); - buildersStack.Pop(); + _constructorBuildersStackIndex--; newAspect._source = world; builder.Build(out newAspect._mask); newAspect._isBuilt = true; - return (TAspect)newAspect; + return newAspect; } #endregion @@ -177,13 +158,13 @@ namespace DCFApixels.DragonECS public TOtherAspect Combine(int order = 0) where TOtherAspect : EcsAspect, new() { var result = _world.GetAspect(); - _maskBuilder.Combine(result.Mask); + _maskBuilder.Combine(result.Mask._staticMask); return result; } public TOtherAspect Except(int order = 0) where TOtherAspect : EcsAspect, new() { var result = _world.GetAspect(); - _maskBuilder.Except(result.Mask); + _maskBuilder.Except(result.Mask._staticMask); return result; } #endregion @@ -191,7 +172,7 @@ namespace DCFApixels.DragonECS #region Build private void Build(out EcsMask mask) { - mask = _maskBuilder.Build(); + mask = _maskBuilder.Build().ToMask(_world); } #endregion diff --git a/src/EcsMask.cs b/src/EcsMask.cs index 34d8671..597b015 100644 --- a/src/EcsMask.cs +++ b/src/EcsMask.cs @@ -1,6 +1,5 @@ using DCFApixels.DragonECS.Internal; using System; -using System.Collections.Concurrent; using System.Collections.Generic; using System.Diagnostics; using System.Linq; @@ -16,176 +15,6 @@ namespace DCFApixels.DragonECS { EcsMask ToMask(EcsWorld world); } -#if ENABLE_IL2CPP - [Il2CppSetOption (Option.NullChecks, false)] - [Il2CppSetOption(Option.ArrayBoundsChecks, false)] -#endif - public sealed class EcsStaticMask : IEquatable, IEcsComponentMask - { - private static ConcurrentDictionary _ids = new ConcurrentDictionary(); - private static IdDispenser _idDIspenser = new IdDispenser(nullID: 0); - private static object _lock = new object(); - - private readonly int _id; - /// Sorted - private readonly EcsTypeCode[] _inc; - /// Sorted - private readonly EcsTypeCode[] _exc; - - #region Properties - public int ID - { - [MethodImpl(MethodImplOptions.AggressiveInlining)] - get { return _id; } - } - /// Sorted set including constraints presented as global type codes. - public ReadOnlySpan IncTypeCodes - { - [MethodImpl(MethodImplOptions.AggressiveInlining)] - get { return _inc; } - } - /// Sorted set excluding constraints presented as global type codes. - public ReadOnlySpan ExcTypeCodes - { - [MethodImpl(MethodImplOptions.AggressiveInlining)] - get { return _exc; } - } - #endregion - - #region Constrcutors - public static Builder New() { return new Builder(); } - public static Builder Inc() { return new Builder().Inc(); } - public static Builder Exc() { return new Builder().Exc(); } - private EcsStaticMask(int id, Key key) - { - _id = id; - _inc = key.inc; - _exc = key.exc; - } - #endregion - - #region Methods - public EcsMask ToMask(EcsWorld world) - { - return EcsMask.FromStatic(world, this); - } - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public bool Equals(EcsStaticMask other) { return _id == other._id; } - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public override int GetHashCode() { return _id; } - public override bool Equals(object obj) { return Equals((EcsStaticMask)obj); } - public override string ToString() { return $"s_mask({_id})"; } - #endregion - - #region Builder - private readonly struct Key : IEquatable - { - public readonly EcsTypeCode[] inc; - public readonly EcsTypeCode[] exc; - public readonly int hash; - - #region Constructors - public Key(EcsTypeCode[] inc, EcsTypeCode[] exc) - { - this.inc = inc; - this.exc = exc; - unchecked - { - hash = inc.Length + exc.Length; - for (int i = 0, iMax = inc.Length; i < iMax; i++) - { - hash = hash * EcsConsts.MAGIC_PRIME + (int)inc[i]; - } - for (int i = 0, iMax = exc.Length; i < iMax; i++) - { - hash = hash * EcsConsts.MAGIC_PRIME - (int)exc[i]; - } - } - } - #endregion - - #region Object - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public bool Equals(Key other) - { - if (inc.Length != other.inc.Length) { return false; } - if (exc.Length != other.exc.Length) { return false; } - for (int i = 0; i < inc.Length; i++) - { - if (inc[i] != other.inc[i]) { return false; } - } - for (int i = 0; i < exc.Length; i++) - { - if (exc[i] != other.exc[i]) { return false; } - } - return true; - } - public override bool Equals(object obj) { return Equals((Key)obj); } - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public override int GetHashCode() { return hash; } - #endregion - } - public class Builder - { - private readonly HashSet _inc = new HashSet(); - private readonly HashSet _exc = new HashSet(); - - #region Constrcutors - internal Builder() { } - #endregion - - #region Include/Exclude/Combine - public Builder Inc() { return Inc(EcsTypeCodeManager.Get()); } - public Builder Exc() { return Exc(EcsTypeCodeManager.Get()); } - public Builder Inc(Type type) { return Inc(EcsTypeCodeManager.Get(type)); } - public Builder Exc(Type type) { return Exc(EcsTypeCodeManager.Get(type)); } - public Builder Inc(EcsTypeCode typeCode) - { -#if (DEBUG && !DISABLE_DEBUG) || ENABLE_DRAGONECS_ASSERT_CHEKS - if (_inc.Contains(typeCode) || _exc.Contains(typeCode)) { Throw.ConstraintIsAlreadyContainedInMask(); } -#endif - _inc.Add(typeCode); - return this; - } - public Builder Exc(EcsTypeCode typeCode) - { -#if (DEBUG && !DISABLE_DEBUG) || ENABLE_DRAGONECS_ASSERT_CHEKS - if (_inc.Contains(typeCode) || _exc.Contains(typeCode)) { Throw.ConstraintIsAlreadyContainedInMask(); } -#endif - _exc.Add(typeCode); - return this; - } - #endregion - - #region Build - public EcsStaticMask Build() - { - HashSet combinedIncs = _inc; - HashSet combinedExcs = _exc; - - var inc = combinedIncs.ToArray(); - Array.Sort(inc); - var exc = combinedExcs.ToArray(); - Array.Sort(exc); - - var key = new Key(inc, exc); - if (_ids.TryGetValue(key, out EcsStaticMask result) == false) - { - lock (_lock) - { - if (_ids.TryGetValue(key, out result) == false) - { - result = new EcsStaticMask(_idDIspenser.UseFree(), key); - _ids[key] = result; - } - } - } - return result; - } - #endregion - } - #endregion - } #if ENABLE_IL2CPP [Il2CppSetOption (Option.NullChecks, false)] @@ -194,6 +23,7 @@ namespace DCFApixels.DragonECS [DebuggerTypeProxy(typeof(DebuggerProxy))] public sealed class EcsMask : IEquatable, IEcsComponentMask { + internal readonly EcsStaticMask _staticMask; internal readonly int _id; internal readonly short _worldID; internal readonly EcsMaskChunck[] _incChunckMasks; @@ -245,24 +75,19 @@ namespace DCFApixels.DragonECS #endregion #region Constructors + [Obsolete("")]//TODO написать новый сопсоб создания public static Builder New(EcsWorld world) { return new Builder(world); } - internal static EcsMask Create(int id, short worldID, int[] inc, int[] exc) - { -#if DEBUG - CheckConstraints(inc, exc); -#endif - return new EcsMask(id, worldID, inc, exc); - } internal static EcsMask CreateEmpty(int id, short worldID) { - return new EcsMask(id, worldID, new int[0], new int[0]); + return new EcsMask(EcsStaticMask.Empty, id, worldID, new int[0], new int[0]); } internal static EcsMask CreateBroken(int id, short worldID) { - return new EcsMask(id, worldID, new int[1] { 1 }, new int[1] { 1 }); + return new EcsMask(EcsStaticMask.Broken, id, worldID, new int[1] { 1 }, new int[1] { 1 }); } - private EcsMask(int id, short worldID, int[] inc, int[] exc) + private EcsMask(EcsStaticMask staticMask, int id, short worldID, int[] inc, int[] exc) { + _staticMask = staticMask; _id = id; _inc = inc; _exc = exc; @@ -415,33 +240,6 @@ namespace DCFApixels.DragonECS #endregion #region Debug utils -#if DEBUG - private static HashSet _dummyHashSet = new HashSet(); - private static void CheckConstraints(int[] inc, int[] exc) - { - lock (_dummyHashSet) - { - if (CheckRepeats(inc)) { throw new EcsFrameworkException("The values in the Include constraints are repeated."); } - if (CheckRepeats(exc)) { throw new EcsFrameworkException("The values in the Exclude constraints are repeated."); } - _dummyHashSet.Clear(); - _dummyHashSet.UnionWith(inc); - if (_dummyHashSet.Overlaps(exc)) { throw new EcsFrameworkException("Conflicting Include and Exclude constraints."); } - } - } - private static bool CheckRepeats(int[] array) - { - _dummyHashSet.Clear(); - foreach (var item in array) - { - if (_dummyHashSet.Contains(item)) - { - return true; - } - _dummyHashSet.Add(item); - } - return false; - } -#endif private static string CreateLogString(short worldID, int[] inc, int[] exc) { #if (DEBUG && !DISABLE_DEBUG) @@ -558,7 +356,7 @@ namespace DCFApixels.DragonECS #region StaticMask public static EcsMask FromStatic(EcsWorld world, EcsStaticMask abstractMask) { - return world.Get().ConvertFromAbstract(abstractMask); + return world.Get().ConvertFromStatic(abstractMask); } #endregion @@ -566,37 +364,34 @@ namespace DCFApixels.DragonECS private readonly struct WorldMaskComponent : IEcsWorldComponent { private readonly EcsWorld _world; - private readonly Dictionary _masks; private readonly Dictionary _opMasks; - private readonly SparseArray _abstractMasks; + private readonly SparseArray _staticMasks; public readonly EcsMask EmptyMask; public readonly EcsMask BrokenMask; #region Constructor/Destructor - public WorldMaskComponent(EcsWorld world, Dictionary masks, EcsMask emptyMask, EcsMask brokenMask) + public WorldMaskComponent(EcsWorld world) { _world = world; - _masks = masks; _opMasks = new Dictionary(256); - _abstractMasks = new SparseArray(256); - EmptyMask = emptyMask; - BrokenMask = brokenMask; + _staticMasks = new SparseArray(256); + + + + EmptyMask = CreateEmpty(_staticMasks.Count, world.id); + _staticMasks.Add(EmptyMask._staticMask.ID, EmptyMask); + BrokenMask = CreateBroken(_staticMasks.Count, world.id); + _staticMasks.Add(BrokenMask._staticMask.ID, BrokenMask); } public void Init(ref WorldMaskComponent component, EcsWorld world) { - var masks = new Dictionary(256); - EcsMask emptyMask = CreateEmpty(0, world.id); - EcsMask brokenMask = CreateBroken(1, world.id); - masks.Add(new Key(emptyMask._inc, emptyMask._exc), emptyMask); - masks.Add(new Key(brokenMask._inc, brokenMask._exc), brokenMask); - component = new WorldMaskComponent(world, masks, emptyMask, brokenMask); + component = new WorldMaskComponent(world); } public void OnDestroy(ref WorldMaskComponent component, EcsWorld world) { - component._masks.Clear(); component._opMasks.Clear(); - component._abstractMasks.Clear(); + component._staticMasks.Clear(); component = default; } #endregion @@ -611,7 +406,7 @@ namespace DCFApixels.DragonECS { return a.World.Get().BrokenMask; } - result = New(a.World).Combine(a).Combine(b).Build(); + result = ConvertFromStatic(EcsStaticMask.New().Combine(a._staticMask).Combine(b._staticMask).Build()); _opMasks.Add(new OpMaskKey(a._id, b._id, operation), result); } return result; @@ -625,219 +420,69 @@ namespace DCFApixels.DragonECS { return a.World.Get().BrokenMask; } - result = New(a.World).Combine(a).Except(b).Build(); + result = ConvertFromStatic(EcsStaticMask.New().Combine(a._staticMask).Except(b._staticMask).Build()); _opMasks.Add(new OpMaskKey(a._id, b._id, operation), result); } return result; } - internal EcsMask ConvertFromAbstract(EcsStaticMask abstractMask) + + internal EcsMask ConvertFromStatic(EcsStaticMask staticMask) { - if (_abstractMasks.TryGetValue(abstractMask.ID, out EcsMask result) == false) + int[] ConvertTypeCodeToComponentTypeID(ReadOnlySpan from, EcsWorld world) { - var b = New(_world); - foreach (var typeCode in abstractMask.IncTypeCodes) + int[] to = new int[from.Length]; + for (int i = 0; i < to.Length; i++) { - b.Inc(_world.DeclareOrGetComponentTypeID(typeCode)); + to[i] = world.DeclareOrGetComponentTypeID(from[i]); } - foreach (var typeCode in abstractMask.ExcTypeCodes) - { - b.Exc(_world.DeclareOrGetComponentTypeID(typeCode)); - } - result = b.Build(); - _abstractMasks.Add(abstractMask.ID, result); + Array.Sort(to); + return to; } - return result; - } - internal EcsMask GetMask(Key maskKey) - { - if (_masks.TryGetValue(maskKey, out EcsMask result) == false) + + if (_staticMasks.TryGetValue(staticMask.ID, out EcsMask result) == false) { - result = Create(_masks.Count, _world.id, maskKey.inc, maskKey.exc); - _masks.Add(maskKey, result); + int[] incs = ConvertTypeCodeToComponentTypeID(staticMask.IncTypeCodes, _world); + int[] excs = ConvertTypeCodeToComponentTypeID(staticMask.ExcTypeCodes, _world); + + result = new EcsMask(staticMask, _staticMasks.Count, _world.id, incs, excs); + + _staticMasks.Add(staticMask.ID, result); } return result; } #endregion } - private readonly struct Key : IEquatable - { - public readonly int[] inc; - public readonly int[] exc; - public readonly int hash; - - #region Constructors - public Key(int[] inc, int[] exc) - { - this.inc = inc; - this.exc = exc; - unchecked - { - hash = inc.Length + exc.Length; - for (int i = 0, iMax = inc.Length; i < iMax; i++) - { - hash = hash * EcsConsts.MAGIC_PRIME + inc[i]; - } - for (int i = 0, iMax = exc.Length; i < iMax; i++) - { - hash = hash * EcsConsts.MAGIC_PRIME - exc[i]; - } - } - } - #endregion - - #region Object - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public bool Equals(Key other) - { - if (inc.Length != other.inc.Length) { return false; } - if (exc.Length != other.exc.Length) { return false; } - for (int i = 0; i < inc.Length; i++) - { - if (inc[i] != other.inc[i]) { return false; } - } - for (int i = 0; i < exc.Length; i++) - { - if (exc[i] != other.exc[i]) { return false; } - } - return true; - } - public override bool Equals(object obj) { return Equals((Key)obj); } - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public override int GetHashCode() { return hash; } - #endregion - } - - public class Builder + + [Obsolete("")]//TODO написать новый сопсоб создания + public struct Builder { + private readonly EcsStaticMask.Builder _builder; private readonly EcsWorld _world; - private readonly HashSet _inc = new HashSet(); - private readonly HashSet _exc = new HashSet(); - private readonly List _combineds = new List(); - private readonly List _excepteds = new List(); - #region Constrcutors - internal Builder(EcsWorld world) + public Builder(EcsWorld world) { _world = world; - } - #endregion - - #region Inc/Exc/Combine - [Obsolete("Use Inc()")] public Builder Include() { return Inc(); } - [Obsolete("Use Exc()")] public Builder Exclude() { return Exc(); } - [Obsolete("Use Inc(type)")] public Builder Include(Type type) { return Inc(type); } - [Obsolete("Use Exc(type)")] public Builder Exclude(Type type) { return Exc(type); } - - public Builder Inc() { return Inc(_world.GetComponentTypeID()); } - public Builder Exc() { return Exc(_world.GetComponentTypeID()); } - public Builder Inc(Type type) { return Inc(_world.GetComponentTypeID(type)); } - public Builder Exc(Type type) { return Exc(_world.GetComponentTypeID(type)); } - - public Builder Inc(int componentTypeID) - { -#if (DEBUG && !DISABLE_DEBUG) || ENABLE_DRAGONECS_ASSERT_CHEKS - if (_inc.Contains(componentTypeID) || _exc.Contains(componentTypeID)) { Throw.ConstraintIsAlreadyContainedInMask(_world.GetComponentType(componentTypeID)); } -#endif - _inc.Add(componentTypeID); - return this; - } - public Builder Exc(int componentTypeID) - { -#if (DEBUG && !DISABLE_DEBUG) || ENABLE_DRAGONECS_ASSERT_CHEKS - if (_inc.Contains(componentTypeID) || _exc.Contains(componentTypeID)) { Throw.ConstraintIsAlreadyContainedInMask(_world.GetComponentType(componentTypeID)); } -#endif - _exc.Add(componentTypeID); - return this; + _builder = EcsStaticMask.Builder.New(); } - public Builder Combine(EcsMask mask, int order = 0) - { - _combineds.Add(new Combined(mask, order)); - return this; - } + public Builder Include() { return Inc(); } + public Builder Exclude() { return Exc(); } + public Builder Include(Type type) { return Inc(type); } + public Builder Exclude(Type type) { return Exc(type); } - public Builder Except(EcsMask mask, int order = 0) - { - _excepteds.Add(new Excepted(mask, order)); - return this; - } - #endregion + public Builder Inc() { _builder.Inc(); return this; } + public Builder Exc() { _builder.Exc(); return this; } + public Builder Inc(Type type) { _builder.Inc(type); return this; } + public Builder Exc(Type type) { _builder.Exc(type); return this; } + public Builder Inc(EcsTypeCode typeCode) { _builder.Inc(typeCode); return this; } + public Builder Exc(EcsTypeCode typeCode) { _builder.Exc(typeCode); return this; } + public Builder Combine(EcsMask mask) { _builder.Combine(mask._staticMask); return this; } + public Builder Except(EcsMask mask) { _builder.Except(mask._staticMask); return this; } - #region Build - public EcsMask Build() - { - HashSet combinedInc; - HashSet combinedExc; - if (_combineds.Count > 0) - { - combinedInc = new HashSet(); - combinedExc = new HashSet(); - _combineds.Sort((a, b) => a.order - b.order); - foreach (var item in _combineds) - { - EcsMask submask = item.mask; - combinedInc.ExceptWith(submask._exc);//удаляю конфликтующие ограничения - combinedExc.ExceptWith(submask._inc);//удаляю конфликтующие ограничения - combinedInc.UnionWith(submask._inc); - combinedExc.UnionWith(submask._exc); - } - combinedInc.ExceptWith(_exc);//удаляю конфликтующие ограничения - combinedExc.ExceptWith(_inc);//удаляю конфликтующие ограничения - combinedInc.UnionWith(_inc); - combinedExc.UnionWith(_exc); - } - else - { - combinedInc = _inc; - combinedExc = _exc; - } - if (_excepteds.Count > 0) - { - foreach (var item in _excepteds) - { - if (combinedInc.Overlaps(item.mask._exc) || combinedExc.Overlaps(item.mask._inc)) - { - _combineds.Clear(); - _excepteds.Clear(); - return _world.Get().BrokenMask; - } - combinedInc.ExceptWith(item.mask._inc); - combinedExc.ExceptWith(item.mask._exc); - } - } + public Builder Inc(int componentTypeID) { Inc(_world.GetComponentType(componentTypeID)); return this; } + public Builder Exc(int componentTypeID) { Exc(_world.GetComponentType(componentTypeID)); return this; } - var inc = combinedInc.ToArray(); - Array.Sort(inc); - var exc = combinedExc.ToArray(); - Array.Sort(exc); - - _combineds.Clear(); - _excepteds.Clear(); - - return _world.Get().GetMask(new Key(inc, exc)); - } - #endregion - } - - private readonly struct Combined - { - public readonly EcsMask mask; - public readonly int order; - public Combined(EcsMask mask, int order) - { - this.mask = mask; - this.order = order; - } - } - private readonly struct Excepted - { - public readonly EcsMask mask; - public readonly int order; - public Excepted(EcsMask mask, int order) - { - this.mask = mask; - this.order = order; - } + public EcsMask Build() { return _world.Get().ConvertFromStatic(_builder.Build()); } } #endregion } diff --git a/src/EcsPipeline.Builder.cs b/src/EcsPipeline.Builder.cs index 86f5551..4292d83 100644 --- a/src/EcsPipeline.Builder.cs +++ b/src/EcsPipeline.Builder.cs @@ -299,7 +299,7 @@ namespace DCFApixels.DragonECS #region Build #if (DEBUG && !DISABLE_DEBUG) || ENABLE_DRAGONECS_ASSERT_CHEKS - private EcsProfilerMarker _buildBarker = new EcsProfilerMarker("EcsPipeline.Build"); + private static EcsProfilerMarker _buildBarker = new EcsProfilerMarker("EcsPipeline.Build"); #endif public EcsPipeline Build() { diff --git a/src/EcsPipeline.cs b/src/EcsPipeline.cs index 0ebf232..47c82c7 100644 --- a/src/EcsPipeline.cs +++ b/src/EcsPipeline.cs @@ -40,7 +40,7 @@ namespace DCFApixels.DragonECS private bool _isDestoryed = false; #if (DEBUG && !DISABLE_DEBUG) || ENABLE_DRAGONECS_ASSERT_CHEKS - private EcsProfilerMarker _initMarker = new EcsProfilerMarker("EcsPipeline.Init"); + private static EcsProfilerMarker _initMarker = new EcsProfilerMarker("EcsPipeline.Init"); #endif #region Properties @@ -305,16 +305,27 @@ namespace DCFApixels.DragonECS #region EcsProcess [DebuggerTypeProxy(typeof(DebuggerProxy))] - public readonly struct EcsProcessRaw : IEnumerable + public readonly struct EcsProcessRaw : IReadOnlyCollection { + public static readonly EcsProcessRaw Empty = new EcsProcessRaw(Array.Empty()); private readonly Array _systems; #region Properties + public bool IsNullOrEmpty + { + [MethodImpl(MethodImplOptions.AggressiveInlining)] + get { return _systems == null || _systems.Length <= 0; } + } public int Length { [MethodImpl(MethodImplOptions.AggressiveInlining)] get { return _systems.Length; } } + int IReadOnlyCollection.Count + { + [MethodImpl(MethodImplOptions.AggressiveInlining)] + get { return _systems.Length; } + } public IEcsProcess this[int index] { get { return (IEcsProcess)_systems.GetValue(index); } @@ -330,6 +341,11 @@ namespace DCFApixels.DragonECS #endregion #region Enumerator + + IEnumerator IEnumerable.GetEnumerator() + { + return ((IEnumerable)(EcsProcess)this).GetEnumerator(); + } public IEnumerator GetEnumerator() { return _systems.GetEnumerator(); @@ -371,7 +387,7 @@ namespace DCFApixels.DragonECS public readonly struct EcsProcess : IReadOnlyCollection where TProcess : IEcsProcess { - public readonly static EcsProcess Empty = new EcsProcess(Array.Empty()); + public static readonly EcsProcess Empty = new EcsProcess(Array.Empty()); private readonly TProcess[] _systems; #region Properties diff --git a/src/EcsStaticMask.cs b/src/EcsStaticMask.cs new file mode 100644 index 0000000..08bb49f --- /dev/null +++ b/src/EcsStaticMask.cs @@ -0,0 +1,419 @@ +using DCFApixels.DragonECS.Internal; +using System; +using System.Collections.Concurrent; +using System.Collections.Generic; +using System.Diagnostics; +using System.Linq; +using System.Runtime.CompilerServices; +#if ENABLE_IL2CPP +using Unity.IL2CPP.CompilerServices; +#endif + +namespace DCFApixels.DragonECS +{ +#if ENABLE_IL2CPP + [Il2CppSetOption (Option.NullChecks, false)] + [Il2CppSetOption(Option.ArrayBoundsChecks, false)] +#endif + [DebuggerTypeProxy(typeof(DebuggerProxy))] + public sealed class EcsStaticMask : IEquatable, IEcsComponentMask + { + public static readonly EcsStaticMask Empty; + public static readonly EcsStaticMask Broken; + + private static readonly Stack _buildersPool = new Stack(); + private static readonly ConcurrentDictionary _ids = new ConcurrentDictionary(); + private static readonly IdDispenser _idDIspenser = new IdDispenser(nullID: 0); + private static readonly object _lock = new object(); + + static EcsStaticMask() + { + Empty = CreateMask(new Key(new EcsTypeCode[0], new EcsTypeCode[0])); + Broken = CreateMask(new Key(new EcsTypeCode[1] { (EcsTypeCode)1 }, new EcsTypeCode[1] { (EcsTypeCode)1 })); + } + + private readonly int _id; + /// Sorted + private readonly EcsTypeCode[] _inc; + /// Sorted + private readonly EcsTypeCode[] _exc; + + #region Properties + public int ID + { + [MethodImpl(MethodImplOptions.AggressiveInlining)] + get { return _id; } + } + /// Sorted set including constraints presented as global type codes. + public ReadOnlySpan IncTypeCodes + { + [MethodImpl(MethodImplOptions.AggressiveInlining)] + get { return _inc; } + } + /// Sorted set excluding constraints presented as global type codes. + public ReadOnlySpan ExcTypeCodes + { + [MethodImpl(MethodImplOptions.AggressiveInlining)] + get { return _exc; } + } + public bool IsEmpty + { + [MethodImpl(MethodImplOptions.AggressiveInlining)] + get { return _inc.Length == 0 && _exc.Length == 0; } + } + public bool IsBroken + { + get { return (_inc.Length & _exc.Length) == 1 && _inc[0] == _exc[0]; } + } + #endregion + + #region Constrcutors + private static EcsStaticMask CreateMask(Key key) + { + if (_ids.TryGetValue(key, out EcsStaticMask result) == false) + { + lock (_lock) + { + if (_ids.TryGetValue(key, out result) == false) + { + result = new EcsStaticMask(_idDIspenser.UseFree(), key); + _ids[key] = result; + } + } + } + return result; + } + public static Builder New() { return Builder.New(); } + public static Builder Inc() { return Builder.New().Inc(); } + public static Builder Exc() { return Builder.New().Exc(); } + public static Builder Inc(Type type) { return Builder.New().Inc(type); } + public static Builder Exc(Type type) { return Builder.New().Exc(type); } + public static Builder Inc(EcsTypeCode typeCode) { return Builder.New().Inc(typeCode); } + public static Builder Exc(EcsTypeCode typeCode) { return Builder.New().Exc(typeCode); } + private EcsStaticMask(int id, Key key) + { + _id = id; + _inc = key.inc; + _exc = key.exc; + } + #endregion + + #region Methods + public EcsMask ToMask(EcsWorld world) + { + return EcsMask.FromStatic(world, this); + } + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public bool Equals(EcsStaticMask other) { return _id == other._id; } + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public override int GetHashCode() { return _id; } + public override bool Equals(object obj) { return Equals((EcsStaticMask)obj); } + public override string ToString() { return CreateLogString(_inc, _exc); } + #endregion + + #region Builder + private readonly struct Key : IEquatable + { + public readonly EcsTypeCode[] inc; + public readonly EcsTypeCode[] exc; + public readonly int hash; + + #region Constructors + public Key(EcsTypeCode[] inc, EcsTypeCode[] exc) + { + this.inc = inc; + this.exc = exc; + unchecked + { + hash = inc.Length + exc.Length; + for (int i = 0, iMax = inc.Length; i < iMax; i++) + { + hash = hash * EcsConsts.MAGIC_PRIME + (int)inc[i]; + } + for (int i = 0, iMax = exc.Length; i < iMax; i++) + { + hash = hash * EcsConsts.MAGIC_PRIME - (int)exc[i]; + } + } + } + #endregion + + #region Object + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public bool Equals(Key other) + { + if (inc.Length != other.inc.Length) { return false; } + if (exc.Length != other.exc.Length) { return false; } + for (int i = 0; i < inc.Length; i++) + { + if (inc[i] != other.inc[i]) { return false; } + } + for (int i = 0; i < exc.Length; i++) + { + if (exc[i] != other.exc[i]) { return false; } + } + return true; + } + public override bool Equals(object obj) { return Equals((Key)obj); } + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public override int GetHashCode() { return hash; } + #endregion + } + public readonly struct Builder + { + private readonly BuilderInstance _builder; + private readonly int _version; + + public static Builder New() + { + lock (_lock) + { + if (_buildersPool.TryPop(out BuilderInstance builderInstance) == false) + { + builderInstance = new BuilderInstance(); + } + return new Builder(builderInstance); + } + } + private Builder(BuilderInstance builder) + { + _builder = builder; + _version = builder._version; + } + public Builder Inc() { return Inc(EcsTypeCodeManager.Get()); } + public Builder Exc() { return Exc(EcsTypeCodeManager.Get()); } + public Builder Inc(Type type) { return Inc(EcsTypeCodeManager.Get(type)); } + public Builder Exc(Type type) { return Exc(EcsTypeCodeManager.Get(type)); } + public Builder Inc(EcsTypeCode typeCode) + { + if (_version != _builder._version) { Throw.UndefinedException(); } + _builder.Inc(typeCode); + return this; + } + public Builder Exc(EcsTypeCode typeCode) + { + if (_version != _builder._version) { Throw.UndefinedException(); } + _builder.Exc(typeCode); + return this; + } + public Builder Combine(EcsStaticMask mask) + { + if (_version != _builder._version) { Throw.UndefinedException(); } + _builder.Combine(mask); + return this; + } + public Builder Except(EcsStaticMask mask) + { + if (_version != _builder._version) { Throw.UndefinedException(); } + _builder.Except(mask); + return this; + } + + public EcsStaticMask Build() + { + if (_version != _builder._version) { Throw.UndefinedException(); } + lock (_lock) + { + _buildersPool.Push(_builder); + return _builder.Build(); + } + } + } + private class BuilderInstance + { + private readonly HashSet _inc = new HashSet(); + private readonly HashSet _exc = new HashSet(); + private readonly List _combineds = new List(); + private bool _sortedCombinedChecker = true; + private readonly List _excepteds = new List(); + + internal int _version; + + #region Constrcutors + internal BuilderInstance() { } + #endregion + + #region Inc/Exc/Combine/Except + public void Inc(EcsTypeCode typeCode) + { +#if (DEBUG && !DISABLE_DEBUG) || ENABLE_DRAGONECS_ASSERT_CHEKS + if (_inc.Contains(typeCode) || _exc.Contains(typeCode)) { Throw.ConstraintIsAlreadyContainedInMask(); } +#endif + _inc.Add(typeCode); + } + public void Exc(EcsTypeCode typeCode) + { +#if (DEBUG && !DISABLE_DEBUG) || ENABLE_DRAGONECS_ASSERT_CHEKS + if (_inc.Contains(typeCode) || _exc.Contains(typeCode)) { Throw.ConstraintIsAlreadyContainedInMask(); } +#endif + _exc.Add(typeCode); + } + public void Combine(EcsStaticMask mask, int order = 0) + { + if (_sortedCombinedChecker && order != 0) + { + _sortedCombinedChecker = false; + } + _combineds.Add(new Combined(mask, order)); + } + + public void Except(EcsStaticMask mask, int order = 0) + { + _excepteds.Add(new Excepted(mask, order)); + } + #endregion + + #region Build + public EcsStaticMask Build() + { + HashSet combinedIncs = _inc; + HashSet combinedExcs = _exc; + + if (_combineds.Count > 0) + { + combinedIncs = new HashSet(); + combinedExcs = new HashSet(); + if (_sortedCombinedChecker == false) + { + _combineds.Sort((a, b) => a.order - b.order); + } + foreach (var item in _combineds) + { + EcsStaticMask submask = item.mask; + combinedIncs.ExceptWith(submask._exc);//удаляю конфликтующие ограничения + combinedExcs.ExceptWith(submask._inc);//удаляю конфликтующие ограничения + combinedIncs.UnionWith(submask._inc); + combinedExcs.UnionWith(submask._exc); + } + combinedIncs.ExceptWith(_exc);//удаляю конфликтующие ограничения + combinedExcs.ExceptWith(_inc);//удаляю конфликтующие ограничения + combinedIncs.UnionWith(_inc); + combinedExcs.UnionWith(_exc); + _combineds.Clear(); + } + else + { + combinedIncs = _inc; + combinedExcs = _exc; + } + + if (_excepteds.Count > 0) + { + foreach (var item in _excepteds) + { + //if (combinedIncs.Overlaps(item.mask._exc) || combinedExcs.Overlaps(item.mask._inc)) + //{ + // return _world.Get().BrokenMask; + //} + combinedIncs.ExceptWith(item.mask._inc); + combinedExcs.ExceptWith(item.mask._exc); + } + _excepteds.Clear(); + } + + + var inc = combinedIncs.ToArray(); + Array.Sort(inc); + var exc = combinedExcs.ToArray(); + Array.Sort(exc); + + var key = new Key(inc, exc); + EcsStaticMask result = CreateMask(key); + + _inc.Clear(); + _exc.Clear(); + + _version++; + return result; + } + #endregion + + #region Utils + private readonly struct Combined + { + public readonly EcsStaticMask mask; + public readonly int order; + public Combined(EcsStaticMask mask, int order) { this.mask = mask; this.order = order; } + } + private readonly struct Excepted + { + public readonly EcsStaticMask mask; + public readonly int order; + public Excepted(EcsStaticMask mask, int order) { this.mask = mask; this.order = order; } + } + #endregion + } + #endregion + + #region Debug utils + private static string CreateLogString(EcsTypeCode[] inc, EcsTypeCode[] exc) + { +#if (DEBUG && !DISABLE_DEBUG) + string converter(EcsTypeCode o) { return EcsDebugUtility.GetGenericTypeName(EcsTypeCodeManager.FindTypeOfCode(o), 1); } + return $"Inc({string.Join(", ", inc.Select(converter))}) Exc({string.Join(", ", exc.Select(converter))})"; +#else + return $"Inc({string.Join(", ", inc)}) Exc({string.Join(", ", exc)})"; // Release optimization +#endif + } + + internal class DebuggerProxy + { + private EcsStaticMask _source; + + public readonly int ID; + public readonly EcsTypeCode[] included; + public readonly EcsTypeCode[] excluded; + public readonly Type[] includedTypes; + public readonly Type[] excludedTypes; + + public bool IsEmpty { get { return _source.IsEmpty; } } + public bool IsBroken { get { return _source.IsBroken; } } + + public DebuggerProxy(EcsStaticMask mask) + { + _source = mask; + + ID = mask._id; + included = mask._inc; + excluded = mask._exc; + Type converter(EcsTypeCode o) { return EcsTypeCodeManager.FindTypeOfCode(o); } + includedTypes = included.Select(converter).ToArray(); + excludedTypes = excluded.Select(converter).ToArray(); + } + public override string ToString() + { + return CreateLogString(included, excluded); + } + } + +#if DEBUG + //TODO оптимизировать, так как списки сортированны, наверняка есть способ без хешсета пройтись и не локать треды + private static HashSet _dummyHashSet = new HashSet(); + private static void CheckConstraints(int[] inc, int[] exc) + { + lock (_dummyHashSet) + { + if (CheckRepeats(inc)) { throw new EcsFrameworkException("The values in the Include constraints are repeated."); } + if (CheckRepeats(exc)) { throw new EcsFrameworkException("The values in the Exclude constraints are repeated."); } + _dummyHashSet.Clear(); + _dummyHashSet.UnionWith(inc); + if (_dummyHashSet.Overlaps(exc)) { throw new EcsFrameworkException("Conflicting Include and Exclude constraints."); } + } + } + private static bool CheckRepeats(int[] array) + { + _dummyHashSet.Clear(); + foreach (var item in array) + { + if (_dummyHashSet.Contains(item)) + { + return true; + } + _dummyHashSet.Add(item); + } + return false; + } +#endif + #endregion + } +} \ No newline at end of file diff --git a/src/Executors/EcsQueryExecutor.cs b/src/Executors/EcsQueryExecutor.cs index e05dd87..42197dc 100644 --- a/src/Executors/EcsQueryExecutor.cs +++ b/src/Executors/EcsQueryExecutor.cs @@ -85,20 +85,20 @@ namespace DCFApixels.DragonECS return true; } - long* ptr = _versions; + long* versionsPtr = _versions; var slots = _world._poolSlots; foreach (var slotIndex in _maskInc) { - ptr++; - if (*ptr != slots[slotIndex].version) + versionsPtr++; + if (*versionsPtr != slots[slotIndex].version) { return false; } } foreach (var slotIndex in _maskExc) { - ptr++; - if (*ptr != slots[slotIndex].version) + versionsPtr++; + if (*versionsPtr != slots[slotIndex].version) { return false; } diff --git a/src/Internal/EcsTypeCodeManager.cs b/src/Internal/EcsTypeCodeManager.cs index 931ad4f..2862d11 100644 --- a/src/Internal/EcsTypeCodeManager.cs +++ b/src/Internal/EcsTypeCodeManager.cs @@ -38,6 +38,17 @@ namespace DCFApixels.DragonECS.Internal public static EcsTypeCode Get() { return EcsTypeCodeCache.code; } public static bool Has(Type type) { return _codes.ContainsKey(type); } public static bool Has() { return _codes.ContainsKey(typeof(T)); } + public static Type FindTypeOfCode(EcsTypeCode typeCode) + { + foreach (var item in _codes) + { + if (item.Value == typeCode) + { + return item.Key; + } + } + return null; + } public static IEnumerable GetDeclaredTypes() { return _codes.Select(o => new TypeCodeInfo(o.Key, o.Value)); } } #if ENABLE_IL2CPP