From 0871b3d6aff755d8d3250e72b8a217e4cb6b6e3b Mon Sep 17 00:00:00 2001 From: DCFApixels <99481254+DCFApixels@users.noreply.github.com> Date: Tue, 20 May 2025 11:12:30 +0800 Subject: [PATCH] update masks --- src/EcsMask.cs | 74 ++++++++++++++++++++---------- src/EcsStaticMask.cs | 30 +++++++++--- src/EcsWorld.cs | 2 - src/Executors/MaskQueryExecutor.cs | 2 + 4 files changed, 75 insertions(+), 33 deletions(-) diff --git a/src/EcsMask.cs b/src/EcsMask.cs index a8f843e..97e4bbb 100644 --- a/src/EcsMask.cs +++ b/src/EcsMask.cs @@ -46,6 +46,8 @@ namespace DCFApixels.DragonECS /// Sorted internal readonly int[] _anys; + internal readonly EcsMaskFlags _flags; + private EcsMaskIterator _iterator; #region Properties @@ -66,14 +68,19 @@ namespace DCFApixels.DragonECS [MethodImpl(MethodImplOptions.AggressiveInlining)] get { return _excs; } } + public EcsMaskFlags Flags + { + [MethodImpl(MethodImplOptions.AggressiveInlining)] + get { return _flags; } + } public bool IsEmpty { [MethodImpl(MethodImplOptions.AggressiveInlining)] - get { return _incs.Length == 0 && _excs.Length == 0; } + get { return _flags == EcsMaskFlags.Empty; } } public bool IsBroken { - get { return (_incs.Length & _excs.Length) == 1 && _incs[0] == _excs[0]; } + get { return (_flags & EcsMaskFlags.Broken) != 0; } } #endregion @@ -81,20 +88,38 @@ namespace DCFApixels.DragonECS public static Builder New(EcsWorld world) { return new Builder(world); } internal static EcsMask CreateEmpty(int id, short worldID) { - return new EcsMask(EcsStaticMask.Empty, id, worldID, new int[0], new int[0], new int[0]); + return new EcsMask(EcsStaticMask.Empty, id, worldID); } internal static EcsMask CreateBroken(int id, short worldID) { - return new EcsMask(EcsStaticMask.Broken, id, worldID, new int[1] { 1 }, new int[1] { 1 }, new int[0]); + return new EcsMask(EcsStaticMask.Broken, id, worldID); } - private EcsMask(EcsStaticMask staticMask, int id, short worldID, int[] incs, int[] excs, int[] anys) + private EcsMask(EcsStaticMask staticMask, int id, short worldID) { + int[] ConvertTypeCodeToComponentTypeID(ReadOnlySpan from_, EcsWorld world_) + { + int[] to = new int[from_.Length]; + for (int i = 0; i < to.Length; i++) + { + to[i] = world_.DeclareOrGetComponentTypeID(from_[i]); + } + Array.Sort(to); + return to; + } + _staticMask = staticMask; ID = id; + WorldID = worldID; + _flags = staticMask.Flags; + + EcsWorld world = EcsWorld.GetWorld(worldID); + int[] incs = ConvertTypeCodeToComponentTypeID(staticMask.IncTypeCodes, world); + int[] excs = ConvertTypeCodeToComponentTypeID(staticMask.ExcTypeCodes, world); + int[] anys = ConvertTypeCodeToComponentTypeID(staticMask.AnyTypeCodes, world); + _incs = incs; _excs = excs; _anys = anys; - WorldID = worldID; _incChunckMasks = MakeMaskChuncsArray(incs); _excChunckMasks = MakeMaskChuncsArray(excs); @@ -314,25 +339,11 @@ namespace DCFApixels.DragonECS internal EcsMask ConvertFromStatic(EcsStaticMask staticMask) { - int[] ConvertTypeCodeToComponentTypeID(ReadOnlySpan from, EcsWorld world) - { - int[] to = new int[from.Length]; - for (int i = 0; i < to.Length; i++) - { - to[i] = world.DeclareOrGetComponentTypeID(from[i]); - } - Array.Sort(to); - return to; - } + if (_staticMasks.TryGetValue(staticMask.ID, out EcsMask result) == false) { - int[] incs = ConvertTypeCodeToComponentTypeID(staticMask.IncTypeCodes, _world); - int[] excs = ConvertTypeCodeToComponentTypeID(staticMask.ExcTypeCodes, _world); - int[] anys = ConvertTypeCodeToComponentTypeID(staticMask.AnyTypeCodes, _world); - - result = new EcsMask(staticMask, _staticMasks.Count, _world.ID, incs, excs, anys); - + result = new EcsMask(staticMask, _staticMasks.Count, _world.ID); _staticMasks.Add(staticMask.ID, result); } return result; @@ -368,14 +379,13 @@ namespace DCFApixels.DragonECS #endregion #region Debug utils - //TODO доработать дебаг с учетом Any private static string CreateLogString(short worldID, int[] incs, int[] excs, int[] anys) { #if DEBUG string converter(int o) { return EcsDebugUtility.GetGenericTypeName(EcsWorld.GetWorld(worldID).AllPools[o].ComponentType, 1); } - return $"Inc({string.Join(", ", incs.Select(converter))}) Exc({string.Join(", ", excs.Select(converter))}) Any({string.Join(", ", anys.Select(converter))})"; + return $"Inc({string.Join(", ", incs.Select(converter))}); Exc({string.Join(", ", excs.Select(converter))}); Any({string.Join(", ", anys.Select(converter))})"; #else - return $"Inc({string.Join(", ", inc)}) Exc({string.Join(", ", exc)})"; // Release optimization + return $"Inc({string.Join(", ", incs)}); Exc({string.Join(", ", excs)}; Any({string.Join(", ", anys)})"; // Release optimization #endif } @@ -453,6 +463,20 @@ namespace DCFApixels.DragonECS #endregion } + [Flags] + public enum EcsMaskFlags : byte + { + Empty = 0, + Inc = 1 << 0, + Exc = 1 << 1, + Any = 1 << 2, + IncExc = Inc | Exc, + IncAny = Inc | Any, + ExcAny = Exc | Any, + IncExcAny = Inc | Exc | Any, + Broken = IncExcAny + 1, + } + #region EcsMaskChunck [DebuggerTypeProxy(typeof(DebuggerProxy))] public readonly struct EcsMaskChunck diff --git a/src/EcsStaticMask.cs b/src/EcsStaticMask.cs index 67234f8..d0610c3 100644 --- a/src/EcsStaticMask.cs +++ b/src/EcsStaticMask.cs @@ -50,6 +50,8 @@ namespace DCFApixels.DragonECS /// Sorted private readonly EcsTypeCode[] _anys; + private readonly EcsMaskFlags _flags; + #region Properties /// Sorted set including constraints presented as global type codes. public ReadOnlySpan IncTypeCodes @@ -69,14 +71,19 @@ namespace DCFApixels.DragonECS [MethodImpl(MethodImplOptions.AggressiveInlining)] get { return _anys; } } + public EcsMaskFlags Flags + { + [MethodImpl(MethodImplOptions.AggressiveInlining)] + get { return _flags; } + } public bool IsEmpty { [MethodImpl(MethodImplOptions.AggressiveInlining)] - get { return _incs.Length == 0 && _excs.Length == 0; } + get { return _flags == EcsMaskFlags.Empty; } } public bool IsBroken { - get { return (_incs.Length & _excs.Length) == 1 && _incs[0] == _excs[0]; } + get { return (_flags & EcsMaskFlags.Broken) != 0; } } #endregion @@ -87,6 +94,13 @@ namespace DCFApixels.DragonECS _incs = key.Incs; _excs = key.Excs; _anys = key.Anys; + if (_incs.Length > 0) { _flags |= EcsMaskFlags.Inc; } + if (_excs.Length > 0) { _flags |= EcsMaskFlags.Exc; } + if (_anys.Length > 0) { _flags |= EcsMaskFlags.Any; } + if ((_incs.Length & _excs.Length) == 1 && _incs[0] == _excs[0]) + { + _flags = EcsMaskFlags.Broken; + } } public static Builder New() { return Builder.New(); } public static Builder Inc() { return Builder.New().Inc(); } @@ -107,7 +121,7 @@ namespace DCFApixels.DragonECS if (_ids.TryGetValue(key, out result) == false) { #if DEBUG - CheckConstraints(key.Incs, key.Excs); //TODO сделать прроверку для key.Anys + CheckConstraints(key.Incs, key.Excs, key.Anys); #endif result = new EcsStaticMask(_idDIspenser.UseFree(), key); _ids[key] = result; @@ -119,6 +133,7 @@ namespace DCFApixels.DragonECS #endregion #region Checks + //TODO доработать проверки с учетом Any public bool IsSubmaskOf(EcsStaticMask otherMask) { return IsSubmask(otherMask, this); @@ -129,11 +144,11 @@ namespace DCFApixels.DragonECS } public bool IsConflictWith(EcsStaticMask otherMask) { - return OverlapsArray(_incs, otherMask._excs) || OverlapsArray(_excs, otherMask._incs); + return OverlapsArray(_incs, otherMask._excs) || OverlapsArray(_excs, otherMask._incs) || OverlapsArray(_anys, otherMask._excs) || OverlapsArray(_anys, otherMask._incs); } private static bool IsSubmask(EcsStaticMask super, EcsStaticMask sub) { - return IsSubarray(sub._incs, super._incs) && IsSuperarray(sub._excs, super._excs); + return IsSubarray(sub._incs, super._incs) && IsSuperarray(sub._excs, super._excs) && IsSubarray(sub._anys, super._anys); } private static bool OverlapsArray(EcsTypeCode[] l, EcsTypeCode[] r) @@ -544,11 +559,14 @@ namespace DCFApixels.DragonECS } #if DEBUG - private static void CheckConstraints(EcsTypeCode[] incs, EcsTypeCode[] excs) + private static void CheckConstraints(EcsTypeCode[] incs, EcsTypeCode[] excs, EcsTypeCode[] anys) { if (CheckRepeats(incs)) { throw new ArgumentException("The values in the Include constraints are repeated."); } if (CheckRepeats(excs)) { throw new ArgumentException("The values in the Exclude constraints are repeated."); } + if (CheckRepeats(anys)) { throw new ArgumentException("The values in the Any constraints are repeated."); } if (OverlapsArray(incs, excs)) { throw new ArgumentException("Conflicting Include and Exclude constraints."); } + if (OverlapsArray(incs, anys)) { throw new ArgumentException("Conflicting Include and Any constraints."); } + if (OverlapsArray(anys, excs)) { throw new ArgumentException("Conflicting Any and Exclude constraints."); } } private static bool CheckRepeats(EcsTypeCode[] array) { diff --git a/src/EcsWorld.cs b/src/EcsWorld.cs index a4cdaf4..51238f8 100644 --- a/src/EcsWorld.cs +++ b/src/EcsWorld.cs @@ -556,8 +556,6 @@ namespace DCFApixels.DragonECS return false; } } - - //TODO оптимизировать if (mask_._anys.Length != 0) { int count = 0; diff --git a/src/Executors/MaskQueryExecutor.cs b/src/Executors/MaskQueryExecutor.cs index 17144fb..f9f82d7 100644 --- a/src/Executors/MaskQueryExecutor.cs +++ b/src/Executors/MaskQueryExecutor.cs @@ -103,6 +103,8 @@ namespace DCFApixels.DragonECS.Core protected abstract void OnDestroy(); } + + //TODO добавить Any public readonly unsafe struct WorldStateVersionsChecker : IDisposable { private readonly EcsWorld _world;