From 09a4870763da00a8c6731febd4152f805a7048c6 Mon Sep 17 00:00:00 2001 From: metalgearsloth <31366439+metalgearsloth@users.noreply.github.com> Date: Fri, 25 Mar 2022 09:20:11 +1100 Subject: [PATCH] Reduce decal allocs (#7226) --- Content.Server/Decals/DecalSystem.cs | 71 ++++++++++++++++++++-- Content.Shared/Decals/SharedDecalSystem.cs | 30 +-------- 2 files changed, 66 insertions(+), 35 deletions(-) diff --git a/Content.Server/Decals/DecalSystem.cs b/Content.Server/Decals/DecalSystem.cs index 2b8b1efc1e..242cbb8cd7 100644 --- a/Content.Server/Decals/DecalSystem.cs +++ b/Content.Server/Decals/DecalSystem.cs @@ -3,6 +3,7 @@ using Content.Server.Administration.Managers; using Content.Shared.Administration; using Content.Shared.Decals; using Content.Shared.Maps; +using Microsoft.Extensions.ObjectPool; using Robust.Server.Player; using Robust.Shared.Enums; using Robust.Shared.Map; @@ -14,10 +15,23 @@ namespace Content.Server.Decals { [Dependency] private readonly IPlayerManager _playerManager = default!; [Dependency] private readonly IAdminManager _adminManager = default!; + [Dependency] private readonly SharedTransformSystem _transform = default!; private readonly Dictionary> _dirtyChunks = new(); private readonly Dictionary>> _previousSentChunks = new(); + // If this ever gets parallelised then you'll want to increase the pooled count. + private ObjectPool> _chunkIndexPool = + new DefaultObjectPool>( + new DefaultPooledObjectPolicy>(), 64); + + private ObjectPool>> _chunkViewerPool = + new DefaultObjectPool>>( + new DefaultPooledObjectPolicy>>(), 64); + + // Pool if we ever parallelise. + private HashSet _viewers = new(64); + public override void Initialize() { base.Initialize(); @@ -299,14 +313,15 @@ namespace Content.Server.Decals foreach (var session in Filter.Broadcast().Recipients) { - if(session is not IPlayerSession playerSession || playerSession.Status != SessionStatus.InGame) + if (session is not IPlayerSession { Status: SessionStatus.InGame } playerSession) continue; var chunks = GetChunksForSession(playerSession); - var updatedChunks = new Dictionary>(); + var updatedChunks = _chunkViewerPool.Get(); foreach (var (gridId, gridChunks) in chunks) { - var newChunks = new HashSet(gridChunks); + var newChunks = _chunkIndexPool.Get(); + newChunks.UnionWith(gridChunks); if (_previousSentChunks[playerSession].TryGetValue(gridId, out var previousChunks)) { newChunks.ExceptWith(previousChunks); @@ -324,9 +339,15 @@ namespace Content.Server.Decals updatedChunks[gridId] = newChunks; } - if(updatedChunks.Count == 0) + if (updatedChunks.Count == 0) + { + ReturnToPool(chunks); + // Even if updatedChunks is empty we'll still return it to the pool as it may have been allocated higher. + ReturnToPool(updatedChunks); continue; + } + ReturnToPool(_previousSentChunks[playerSession]); _previousSentChunks[playerSession] = chunks; //send all gridChunks to client @@ -336,6 +357,16 @@ namespace Content.Server.Decals _dirtyChunks.Clear(); } + private void ReturnToPool(Dictionary> chunks) + { + foreach (var (_, previous) in chunks) + { + _chunkIndexPool.Return(previous); + } + + _chunkViewerPool.Return(chunks); + } + private void SendChunkUpdates(IPlayerSession session, Dictionary> updatedChunks) { var updatedDecals = new Dictionary>>(); @@ -357,7 +388,7 @@ namespace Content.Server.Decals private HashSet GetSessionViewers(IPlayerSession session) { - var viewers = new HashSet(); + var viewers = _viewers; if (session.Status != SessionStatus.InGame || session.AttachedEntity is null) return viewers; @@ -373,7 +404,35 @@ namespace Content.Server.Decals private Dictionary> GetChunksForSession(IPlayerSession session) { - return GetChunksForViewers(GetSessionViewers(session)); + var viewers = GetSessionViewers(session); + var chunks = GetChunksForViewers(viewers); + viewers.Clear(); + return chunks; + } + + private Dictionary> GetChunksForViewers(HashSet viewers) + { + var chunks = _chunkViewerPool.Get(); + var xformQuery = GetEntityQuery(); + + foreach (var viewerUid in viewers) + { + var (bounds, mapId) = CalcViewBounds(viewerUid, xformQuery.GetComponent(viewerUid)); + + foreach (var grid in MapManager.FindGridsIntersecting(mapId, bounds)) + { + if (!chunks.ContainsKey(grid.Index)) + chunks[grid.Index] = _chunkIndexPool.Get(); + + var enumerator = new ChunkIndicesEnumerator(_transform.GetInvWorldMatrix(grid.GridEntityId, xformQuery).TransformBox(bounds), ChunkSize); + + while (enumerator.MoveNext(out var indices)) + { + chunks[grid.Index].Add(indices.Value); + } + } + } + return chunks; } } } diff --git a/Content.Shared/Decals/SharedDecalSystem.cs b/Content.Shared/Decals/SharedDecalSystem.cs index e71679e864..4d8b805686 100644 --- a/Content.Shared/Decals/SharedDecalSystem.cs +++ b/Content.Shared/Decals/SharedDecalSystem.cs @@ -12,7 +12,6 @@ namespace Content.Shared.Decals [Dependency] protected readonly IPrototypeManager PrototypeManager = default!; [Dependency] private readonly IConfigurationManager _configurationManager = default!; [Dependency] protected readonly IMapManager MapManager = default!; - [Dependency] protected readonly SharedTransformSystem Transforms = default!; protected readonly Dictionary> ChunkIndex = new(); @@ -84,40 +83,13 @@ namespace Content.Shared.Decals protected virtual bool RemoveDecalHook(GridId gridId, uint uid) => true; - private (Box2 view, MapId mapId) CalcViewBounds(in EntityUid euid) + protected (Box2 view, MapId mapId) CalcViewBounds(in EntityUid euid, TransformComponent xform) { - var xform = EntityManager.GetComponent(euid); - var view = Box2.UnitCentered.Scale(_viewSize).Translated(xform.WorldPosition); var map = xform.MapID; return (view, map); } - - protected Dictionary> GetChunksForViewers(HashSet viewers) - { - var chunks = new Dictionary>(); - var xformQuery = GetEntityQuery(); - - foreach (var viewerUid in viewers) - { - var (bounds, mapId) = CalcViewBounds(viewerUid); - - foreach (var grid in MapManager.FindGridsIntersecting(mapId, bounds)) - { - if (!chunks.ContainsKey(grid.Index)) - chunks[grid.Index] = new HashSet(); - - var enumerator = new ChunkIndicesEnumerator(Transforms.GetInvWorldMatrix(grid.GridEntityId, xformQuery).TransformBox(bounds), ChunkSize); - - while (enumerator.MoveNext(out var indices)) - { - chunks[grid.Index].Add(indices.Value); - } - } - } - return chunks; - } } // TODO: Pretty sure paul was moving this somewhere but just so people know