From 161b1874c2c45ee52db02bd0f1627bc0686338b6 Mon Sep 17 00:00:00 2001 From: metalgearsloth <31366439+metalgearsloth@users.noreply.github.com> Date: Sun, 30 Jul 2023 03:34:35 +1000 Subject: [PATCH] Some comp query optimisations (#4203) --- Robust.Server/Bql/BqlQuerySelector.Builtin.cs | 2 +- .../GameObjects/EntityManager.Components.cs | 14 +- .../GameObjects/IEntityManager.Components.cs | 2 +- .../Systems/EntityLookup.Queries.cs | 26 ++- .../EntityLookupSystem.ComponentQueries.cs | 201 +++++++++++++----- 5 files changed, 175 insertions(+), 70 deletions(-) diff --git a/Robust.Server/Bql/BqlQuerySelector.Builtin.cs b/Robust.Server/Bql/BqlQuerySelector.Builtin.cs index b896c95d8be..1f701ad3519 100644 --- a/Robust.Server/Bql/BqlQuerySelector.Builtin.cs +++ b/Robust.Server/Bql/BqlQuerySelector.Builtin.cs @@ -30,7 +30,7 @@ public override IEnumerable DoInitialSelection(IReadOnlyList } return entityManager.GetAllComponents((Type) arguments[0], includePaused: true) - .Select(x => x.Owner); + .Select(x => x.Uid); } } diff --git a/Robust.Shared/GameObjects/EntityManager.Components.cs b/Robust.Shared/GameObjects/EntityManager.Components.cs index 78c3c54de1a..d1334ffaf7e 100644 --- a/Robust.Shared/GameObjects/EntityManager.Components.cs +++ b/Robust.Shared/GameObjects/EntityManager.Components.cs @@ -1270,28 +1270,26 @@ public IEnumerable EntityQuery(bool includePaused = false) where T : IComp #endregion /// - public IEnumerable GetAllComponents(Type type, bool includePaused = false) + public IEnumerable<(EntityUid Uid, Component Component)> GetAllComponents(Type type, bool includePaused = false) { var comps = _entTraitDict[type]; if (includePaused) { - foreach (var comp in comps.Values) + foreach (var (uid, comp) in comps) { if (comp.Deleted) continue; - yield return comp; + yield return (uid, comp); } } else { - var metaQuery = GetEntityQuery(); - - foreach (var comp in comps.Values) + foreach (var (uid, comp) in comps) { - if (comp.Deleted || !metaQuery.TryGetComponent(comp.Owner, out var meta) || meta.EntityPaused) continue; + if (comp.Deleted || !_metaQuery.TryGetComponent(uid, out var meta) || meta.EntityPaused) continue; - yield return comp; + yield return (uid, comp); } } } diff --git a/Robust.Shared/GameObjects/IEntityManager.Components.cs b/Robust.Shared/GameObjects/IEntityManager.Components.cs index b768290d157..07b7b50320e 100644 --- a/Robust.Shared/GameObjects/IEntityManager.Components.cs +++ b/Robust.Shared/GameObjects/IEntityManager.Components.cs @@ -488,7 +488,7 @@ EntityQueryEnumerator EntityQueryEnumeratorA trait or component type to check for. /// /// All components that are the specified type. - IEnumerable GetAllComponents(Type type, bool includePaused = false); + IEnumerable<(EntityUid Uid, Component Component)> GetAllComponents(Type type, bool includePaused = false); /// /// Culls all components from the collection that are marked as deleted. This needs to be called often. diff --git a/Robust.Shared/GameObjects/Systems/EntityLookup.Queries.cs b/Robust.Shared/GameObjects/Systems/EntityLookup.Queries.cs index fe7db6e6b73..929adc0856d 100644 --- a/Robust.Shared/GameObjects/Systems/EntityLookup.Queries.cs +++ b/Robust.Shared/GameObjects/Systems/EntityLookup.Queries.cs @@ -144,6 +144,9 @@ private bool AnyEntitiesIntersecting(EntityUid lookupUid, tuple.found = true; return false; }, localAABB, (flags & LookupFlags.Approximate) != 0x0); + + if (state.found) + return true; } if ((flags & LookupFlags.Static) != 0x0) @@ -156,6 +159,9 @@ private bool AnyEntitiesIntersecting(EntityUid lookupUid, tuple.found = true; return false; }, localAABB, (flags & LookupFlags.Approximate) != 0x0); + + if (state.found) + return true; } if ((flags & LookupFlags.StaticSundries) == LookupFlags.StaticSundries) @@ -168,6 +174,9 @@ private bool AnyEntitiesIntersecting(EntityUid lookupUid, tuple.found = true; return false; }, localAABB, (flags & LookupFlags.Approximate) != 0x0); + + if (state.found) + return true; } if ((flags & LookupFlags.Sundries) != 0x0) @@ -182,10 +191,7 @@ private bool AnyEntitiesIntersecting(EntityUid lookupUid, }, localAABB, (flags & LookupFlags.Approximate) != 0x0); } - if (state.found) - return true; - - return false; + return state.found; } private bool AnyEntitiesIntersecting(EntityUid lookupUid, @@ -209,6 +215,9 @@ private bool AnyEntitiesIntersecting(EntityUid lookupUid, }, localAABB, (flags & LookupFlags.Approximate) != 0x0); } + if (state.found) + return true; + if ((flags & LookupFlags.Static) != 0x0) { lookup.StaticTree.QueryAabb(ref state, (ref (EntityUid? ignored, bool found) tuple, in FixtureProxy value) => @@ -221,6 +230,9 @@ private bool AnyEntitiesIntersecting(EntityUid lookupUid, }, localAABB, (flags & LookupFlags.Approximate) != 0x0); } + if (state.found) + return true; + if ((flags & LookupFlags.StaticSundries) == LookupFlags.StaticSundries) { lookup.StaticSundriesTree.QueryAabb(ref state, static (ref (EntityUid? ignored, bool found) tuple, in EntityUid value) => @@ -233,6 +245,9 @@ private bool AnyEntitiesIntersecting(EntityUid lookupUid, }, localAABB, (flags & LookupFlags.Approximate) != 0x0); } + if (state.found) + return true; + if ((flags & LookupFlags.Sundries) != 0x0) { lookup.SundriesTree.QueryAabb(ref state, static (ref (EntityUid? ignored, bool found) tuple, in EntityUid value) => @@ -245,9 +260,6 @@ private bool AnyEntitiesIntersecting(EntityUid lookupUid, }, localAABB, (flags & LookupFlags.Approximate) != 0x0); } - if (state.found) - return true; - return state.found; } diff --git a/Robust.Shared/GameObjects/Systems/EntityLookupSystem.ComponentQueries.cs b/Robust.Shared/GameObjects/Systems/EntityLookupSystem.ComponentQueries.cs index 8531dddf580..bcc215d8640 100644 --- a/Robust.Shared/GameObjects/Systems/EntityLookupSystem.ComponentQueries.cs +++ b/Robust.Shared/GameObjects/Systems/EntityLookupSystem.ComponentQueries.cs @@ -4,6 +4,7 @@ using Robust.Shared.Collections; using Robust.Shared.Containers; using Robust.Shared.Map; +using Robust.Shared.Map.Components; using Robust.Shared.Maths; using Robust.Shared.Physics; using Robust.Shared.Physics.Dynamics; @@ -20,12 +21,10 @@ private void AddComponentsIntersecting( HashSet intersecting, Box2 worldAABB, LookupFlags flags, - EntityQuery lookupQuery, - EntityQuery xformQuery, EntityQuery query) where T : Component { - var lookup = lookupQuery.GetComponent(lookupUid); - var invMatrix = _transform.GetInvWorldMatrix(lookupUid, xformQuery); + var lookup = _broadQuery.GetComponent(lookupUid); + var invMatrix = _transform.GetInvWorldMatrix(lookupUid); var localAABB = invMatrix.TransformBox(worldAABB); var state = (intersecting, query); @@ -78,9 +77,85 @@ private void AddComponentsIntersecting( } } - private void RecursiveAdd(EntityUid uid, ref ValueList toAdd, EntityQuery xformQuery, EntityQuery query) where T : Component + private bool AnyComponentsIntersecting( + EntityUid lookupUid, + Box2 worldAABB, + LookupFlags flags, + EntityQuery query, + EntityUid? ignored = null) where T : Component { - var childEnumerator = xformQuery.GetComponent(uid).ChildEnumerator; + var lookup = _broadQuery.GetComponent(lookupUid); + var invMatrix = _transform.GetInvWorldMatrix(lookupUid); + var localAABB = invMatrix.TransformBox(worldAABB); + var state = (query, ignored, found: false); + + if ((flags & LookupFlags.Dynamic) != 0x0) + { + lookup.DynamicTree.QueryAabb(ref state, + static (ref (EntityQuery query, EntityUid? ignored, bool found) tuple, in FixtureProxy value) => + { + if (value.Entity == tuple.ignored) + return true; + + tuple.found = true; + return false; + }, localAABB, (flags & LookupFlags.Approximate) != 0x0); + + if (state.found) + return true; + } + + if ((flags & LookupFlags.Static) != 0x0) + { + lookup.StaticTree.QueryAabb(ref state, + static (ref (EntityQuery query, EntityUid? ignored, bool found) tuple, in FixtureProxy value) => + { + if (value.Entity == tuple.ignored) + return true; + + tuple.found = true; + return false; + }, localAABB, (flags & LookupFlags.Approximate) != 0x0); + + if (state.found) + return true; + } + + if ((flags & LookupFlags.StaticSundries) == LookupFlags.StaticSundries) + { + lookup.StaticSundriesTree.QueryAabb(ref state, + static (ref (EntityQuery query, EntityUid? ignored, bool found) tuple, in EntityUid value) => + { + if (value == tuple.ignored) + return true; + + tuple.found = true; + return false; + }, localAABB, (flags & LookupFlags.Approximate) != 0x0); + + if (state.found) + return true; + } + + if ((flags & LookupFlags.Sundries) != 0x0) + { + lookup.SundriesTree.QueryAabb(ref state, + static (ref (EntityQuery query, EntityUid? ignored, bool found) tuple, in EntityUid value) => + { + if (value == tuple.ignored) + return true; + + tuple.found = true; + return false; + }, localAABB, (flags & LookupFlags.Approximate) != 0x0); + } + + return state.found; + } + + private void RecursiveAdd(EntityUid uid, ref ValueList toAdd, EntityQuery query) where T : Component + { + var childEnumerator = _xformQuery.GetComponent(uid).ChildEnumerator; while (childEnumerator.MoveNext(out var child)) { @@ -89,20 +164,19 @@ private void RecursiveAdd(EntityUid uid, ref ValueList toAdd, EntityQuery< toAdd.Add(compies); } - RecursiveAdd(child.Value, ref toAdd, xformQuery, query); + RecursiveAdd(child.Value, ref toAdd, query); } } - private void AddContained(HashSet intersecting, LookupFlags flags, EntityQuery xformQuery, EntityQuery query) where T : Component + private void AddContained(HashSet intersecting, LookupFlags flags, EntityQuery query) where T : Component { if ((flags & LookupFlags.Contained) == 0x0) return; - var conQuery = GetEntityQuery(); var toAdd = new ValueList(); foreach (var comp in intersecting) { - if (!conQuery.TryGetComponent(comp.Owner, out var conManager)) continue; + if (!_containerQuery.TryGetComponent(comp.Owner, out var conManager)) continue; foreach (var con in conManager.GetAllContainers()) { @@ -113,7 +187,7 @@ private void AddContained(HashSet intersecting, LookupFlags flags, EntityQ toAdd.Add(compies); } - RecursiveAdd(contained, ref toAdd, xformQuery, query); + RecursiveAdd(contained, ref toAdd, query); } } } @@ -148,26 +222,21 @@ private bool UseBoundsQuery(float area) where T : Component // Like .Queries but works with components #region Box2 - public bool AnyComponentsIntersecting(Type type, MapId mapId, Box2 worldAABB, LookupFlags flags = DefaultFlags) + public bool AnyComponentsIntersecting(Type type, MapId mapId, Box2 worldAABB, EntityUid? ignored = null, LookupFlags flags = DefaultFlags) { DebugTools.Assert(typeof(Component).IsAssignableFrom(type)); if (mapId == MapId.Nullspace) return false; - var xformQuery = GetEntityQuery(); - var intersecting = new HashSet(); - if (!UseBoundsQuery(type, worldAABB.Height * worldAABB.Width)) { - var metaQuery = GetEntityQuery(); - - foreach (var comp in EntityManager.GetAllComponents(type, true)) + foreach (var (uid, comp) in EntityManager.GetAllComponents(type, true)) { - var xform = xformQuery.GetComponent(comp.Owner); + var xform = _xformQuery.GetComponent(uid); if (xform.MapID != mapId || - !worldAABB.Contains(_transform.GetWorldPosition(comp.Owner, xformQuery)) || + !worldAABB.Contains(_transform.GetWorldPosition(uid)) || ((flags & LookupFlags.Contained) == 0x0 && - _container.IsEntityOrParentInContainer(comp.Owner, metaQuery.GetComponent(comp.Owner), xform, metaQuery, xformQuery))) + _container.IsEntityOrParentInContainer(uid, _metaQuery.GetComponent(uid), xform, _metaQuery, _xformQuery))) { continue; } @@ -178,63 +247,81 @@ public bool AnyComponentsIntersecting(Type type, MapId mapId, Box2 worldAABB, Lo else { var query = EntityManager.GetEntityQuery(type); - var lookupQuery = GetEntityQuery(); + // Get grid entities - foreach (var grid in _mapManager.FindGridsIntersecting(mapId, worldAABB)) - { - AddComponentsIntersecting(grid.Owner, intersecting, worldAABB, flags, lookupQuery, xformQuery, query); - } + var state = (this, worldAABB, flags, query, ignored, found: false); + + _mapManager.FindGridsIntersecting(mapId, worldAABB, ref state, + static (EntityUid uid, MapGridComponent grid, ref + (EntityLookupSystem system, + Box2 worldAABB, + LookupFlags flags, + EntityQuery query, + EntityUid? ignored, + bool found) tuple) => + { + if (!tuple.system.AnyComponentsIntersecting(uid, tuple.worldAABB, tuple.flags, tuple.query, tuple.ignored)) + return true; + tuple.found = true; + return false; + }, (flags & LookupFlags.Approximate) != 0x0); // Get map entities var mapUid = _mapManager.GetMapEntityId(mapId); - AddComponentsIntersecting(mapUid, intersecting, worldAABB, flags, lookupQuery, xformQuery, query); - AddContained(intersecting, flags, xformQuery, query); + AnyComponentsIntersecting(mapUid, worldAABB, flags, query, ignored); } - return intersecting.Count > 0; + return false; } public HashSet GetComponentsIntersecting(Type type, MapId mapId, Box2 worldAABB, LookupFlags flags = DefaultFlags) { DebugTools.Assert(typeof(Component).IsAssignableFrom(type)); - if (mapId == MapId.Nullspace) return new HashSet(); + if (mapId == MapId.Nullspace) + return new HashSet(); - var xformQuery = GetEntityQuery(); var intersecting = new HashSet(); if (!UseBoundsQuery(type, worldAABB.Height * worldAABB.Width)) { - var metaQuery = GetEntityQuery(); - - foreach (var comp in EntityManager.GetAllComponents(type, true)) + foreach (var (uid, comp) in EntityManager.GetAllComponents(type, true)) { - var xform = xformQuery.GetComponent(comp.Owner); + var xform = _xformQuery.GetComponent(uid); if (xform.MapID != mapId || - !worldAABB.Contains(_transform.GetWorldPosition(comp.Owner, xformQuery)) || + !worldAABB.Contains(_transform.GetWorldPosition(uid)) || ((flags & LookupFlags.Contained) == 0x0 && - _container.IsEntityOrParentInContainer(comp.Owner, metaQuery.GetComponent(comp.Owner), xform, metaQuery, xformQuery))) + _container.IsEntityOrParentInContainer(uid, _metaQuery.GetComponent(uid), xform, _metaQuery, _xformQuery))) { continue; } - intersecting.Add((Component) comp); + intersecting.Add(comp); } } else { var query = EntityManager.GetEntityQuery(type); - var lookupQuery = GetEntityQuery(); + // Get grid entities - foreach (var grid in _mapManager.FindGridsIntersecting(mapId, worldAABB)) - { - AddComponentsIntersecting(grid.Owner, intersecting, worldAABB, flags, lookupQuery, xformQuery, query); - } + var state = (this, worldAABB, flags, query, intersecting); + + _mapManager.FindGridsIntersecting(mapId, worldAABB, ref state, + static (EntityUid uid, MapGridComponent grid, + ref (EntityLookupSystem system, + Box2 worldAABB, + LookupFlags flags, + EntityQuery query, + HashSet intersecting) tuple) => + { + tuple.system.AddComponentsIntersecting(uid, tuple.intersecting, tuple.worldAABB, tuple.flags, tuple.query); + return true; + }, (flags & LookupFlags.Approximate) != 0x0); // Get map entities var mapUid = _mapManager.GetMapEntityId(mapId); - AddComponentsIntersecting(mapUid, intersecting, worldAABB, flags, lookupQuery, xformQuery, query); - AddContained(intersecting, flags, xformQuery, query); + AddComponentsIntersecting(mapUid, intersecting, worldAABB, flags, query); + AddContained(intersecting, flags, query); } return intersecting; @@ -244,7 +331,6 @@ public HashSet GetComponentsIntersecting(MapId mapId, Box2 worldAABB, Look { if (mapId == MapId.Nullspace) return new HashSet(); - var xformQuery = GetEntityQuery(); var intersecting = new HashSet(); if (!UseBoundsQuery(worldAABB.Height * worldAABB.Width)) @@ -253,24 +339,33 @@ public HashSet GetComponentsIntersecting(MapId mapId, Box2 worldAABB, Look while (query.MoveNext(out var comp, out var xform)) { - if (xform.MapID != mapId || !worldAABB.Contains(_transform.GetWorldPosition(xform, xformQuery))) continue; + if (xform.MapID != mapId || !worldAABB.Contains(_transform.GetWorldPosition(xform))) continue; intersecting.Add(comp); } } else { var query = GetEntityQuery(); - var lookupQuery = GetEntityQuery(); + // Get grid entities - foreach (var grid in _mapManager.FindGridsIntersecting(mapId, worldAABB)) + var state = (this, worldAABB, flags, query, intersecting); + + _mapManager.FindGridsIntersecting(mapId, worldAABB, ref state, + static (EntityUid uid, MapGridComponent grid, + ref (EntityLookupSystem system, + Box2 worldAABB, + LookupFlags flags, + EntityQuery query, + HashSet intersecting) tuple) => { - AddComponentsIntersecting(grid.Owner, intersecting, worldAABB, flags, lookupQuery, xformQuery, query); - } + tuple.system.AddComponentsIntersecting(uid, tuple.intersecting, tuple.worldAABB, tuple.flags, tuple.query); + return true; + }, (flags & LookupFlags.Approximate) != 0x0); // Get map entities var mapUid = _mapManager.GetMapEntityId(mapId); - AddComponentsIntersecting(mapUid, intersecting, worldAABB, flags, lookupQuery, xformQuery, query); - AddContained(intersecting, flags, xformQuery, query); + AddComponentsIntersecting(mapUid, intersecting, worldAABB, flags, query); + AddContained(intersecting, flags, query); } return intersecting;