From cd8b5b96b521b71a62b86513847e255bdcebd098 Mon Sep 17 00:00:00 2001 From: metalgearsloth <31366439+metalgearsloth@users.noreply.github.com> Date: Fri, 23 Feb 2024 18:12:23 +1100 Subject: [PATCH] Optimise DecalOverlay (#25266) ChunkSize is still 32 so doesn't cut down on heaps of decals atm though we avoid passing many decals to drawing with the coordinates bounds check now. (cherry picked from commit d8e5f5c24b88bf8096ddaec11e80f1af2953637b) --- Content.Client/Decals/DecalSystem.cs | 19 +--- .../Decals/Overlays/DecalOverlay.cs | 87 ++++++++++++------- Content.Server/Chunking/ChunkingSystem.cs | 1 + Content.Server/Decals/DecalSystem.cs | 1 + Content.Server/Parallax/BiomeSystem.cs | 1 + Content.Shared/Decals/Decal.cs | 2 +- Content.Shared/Decals/DecalGridComponent.cs | 7 +- Content.Shared/Decals/SharedDecalSystem.cs | 33 ------- 8 files changed, 64 insertions(+), 87 deletions(-) diff --git a/Content.Client/Decals/DecalSystem.cs b/Content.Client/Decals/DecalSystem.cs index be442ab8a0..901ab270fb 100644 --- a/Content.Client/Decals/DecalSystem.cs +++ b/Content.Client/Decals/DecalSystem.cs @@ -50,16 +50,8 @@ namespace Content.Client.Decals protected override void OnDecalRemoved(EntityUid gridId, uint decalId, DecalGridComponent component, Vector2i indices, DecalChunk chunk) { base.OnDecalRemoved(gridId, decalId, component, indices, chunk); - - if (!component.DecalZIndexIndex.Remove(decalId, out var zIndex)) - return; - - if (!component.DecalRenderIndex.TryGetValue(zIndex, out var renderIndex)) - return; - - renderIndex.Remove(decalId); - if (renderIndex.Count == 0) - component.DecalRenderIndex.Remove(zIndex); + DebugTools.Assert(chunk.Decals.ContainsKey(decalId)); + chunk.Decals.Remove(decalId); } private void OnHandleState(EntityUid gridUid, DecalGridComponent gridComp, ref ComponentHandleState args) @@ -133,8 +125,6 @@ namespace Content.Client.Decals private void UpdateChunks(EntityUid gridId, DecalGridComponent gridComp, Dictionary updatedGridChunks) { var chunkCollection = gridComp.ChunkCollection.ChunkCollection; - var renderIndex = gridComp.DecalRenderIndex; - var zIndexIndex = gridComp.DecalZIndexIndex; // Update any existing data / remove decals we didn't receive data for. foreach (var (indices, newChunkData) in updatedGridChunks) @@ -155,11 +145,6 @@ namespace Content.Client.Decals foreach (var (uid, decal) in newChunkData.Decals) { - if (zIndexIndex.TryGetValue(uid, out var zIndex)) - renderIndex[zIndex].Remove(uid); - - renderIndex.GetOrNew(decal.ZIndex)[uid] = decal; - zIndexIndex[uid] = decal.ZIndex; gridComp.DecalIndex[uid] = indices; } } diff --git a/Content.Client/Decals/Overlays/DecalOverlay.cs b/Content.Client/Decals/Overlays/DecalOverlay.cs index 8eb1a25664..d9904ae80b 100644 --- a/Content.Client/Decals/Overlays/DecalOverlay.cs +++ b/Content.Client/Decals/Overlays/DecalOverlay.cs @@ -3,6 +3,7 @@ using Robust.Client.GameObjects; using Robust.Client.Graphics; using Robust.Shared.Enums; using Robust.Shared.Map; +using Robust.Shared.Map.Enumerators; using Robust.Shared.Prototypes; namespace Content.Client.Decals.Overlays @@ -15,6 +16,8 @@ namespace Content.Client.Decals.Overlays private readonly Dictionary _cachedTextures = new(64); + private readonly List<(uint Id, Decal Decal)> _decals = new(); + public DecalOverlay( SpriteSystem sprites, IEntityManager entManager, @@ -30,10 +33,10 @@ namespace Content.Client.Decals.Overlays if (args.MapId == MapId.Nullspace) return; - var grid = Grid; + var owner = Grid.Owner; - if (!_entManager.TryGetComponent(grid, out DecalGridComponent? decalGrid) || - !_entManager.TryGetComponent(grid, out TransformComponent? xform)) + if (!_entManager.TryGetComponent(owner, out DecalGridComponent? decalGrid) || + !_entManager.TryGetComponent(owner, out TransformComponent? xform)) { return; } @@ -46,46 +49,68 @@ namespace Content.Client.Decals.Overlays var xformSystem = _entManager.System(); var eyeAngle = args.Viewport.Eye?.Rotation ?? Angle.Zero; - var zIndexDictionary = decalGrid.DecalRenderIndex; + var gridAABB = xformSystem.GetInvWorldMatrix(xform).TransformBox(args.WorldBounds.Enlarged(1f)); + var chunkEnumerator = new ChunkIndicesEnumerator(gridAABB, SharedDecalSystem.ChunkSize); + _decals.Clear(); - if (zIndexDictionary.Count == 0) + while (chunkEnumerator.MoveNext(out var index)) + { + if (!decalGrid.ChunkCollection.ChunkCollection.TryGetValue(index.Value, out var chunk)) + continue; + + foreach (var (id, decal) in chunk.Decals) + { + if (!gridAABB.Contains(decal.Coordinates)) + continue; + + _decals.Add((id, decal)); + } + } + + if (_decals.Count == 0) return; - var (_, worldRot, worldMatrix) = xformSystem.GetWorldPositionRotationMatrix(xform); + _decals.Sort((x, y) => + { + var zComp = x.Decal.ZIndex.CompareTo(y.Decal.ZIndex); + if (zComp != 0) + return zComp; + + return x.Id.CompareTo(y.Id); + }); + + var (_, worldRot, worldMatrix) = xformSystem.GetWorldPositionRotationMatrix(xform); handle.SetTransform(worldMatrix); - foreach (var decals in zIndexDictionary.Values) + foreach (var (_, decal) in _decals) { - foreach (var decal in decals.Values) + if (!_cachedTextures.TryGetValue(decal.Id, out var cache)) { - if (!_cachedTextures.TryGetValue(decal.Id, out var cache)) + // Nothing to cache someone messed up + if (!_prototypeManager.TryIndex(decal.Id, out var decalProto)) { - // Nothing to cache someone messed up - if (!_prototypeManager.TryIndex(decal.Id, out var decalProto)) - { - continue; - } - - cache = (_sprites.Frame0(decalProto.Sprite), decalProto.SnapCardinals); - _cachedTextures[decal.Id] = cache; + continue; } - var cardinal = Angle.Zero; - - if (cache.SnapCardinals) - { - var worldAngle = eyeAngle + worldRot; - cardinal = worldAngle.GetCardinalDir().ToAngle(); - } - - var angle = decal.Angle - cardinal; - - if (angle.Equals(Angle.Zero)) - handle.DrawTexture(cache.Texture, decal.Coordinates, decal.Color); - else - handle.DrawTexture(cache.Texture, decal.Coordinates, angle, decal.Color); + cache = (_sprites.Frame0(decalProto.Sprite), decalProto.SnapCardinals); + _cachedTextures[decal.Id] = cache; } + + var cardinal = Angle.Zero; + + if (cache.SnapCardinals) + { + var worldAngle = eyeAngle + worldRot; + cardinal = worldAngle.GetCardinalDir().ToAngle(); + } + + var angle = decal.Angle - cardinal; + + if (angle.Equals(Angle.Zero)) + handle.DrawTexture(cache.Texture, decal.Coordinates, decal.Color); + else + handle.DrawTexture(cache.Texture, decal.Coordinates, angle, decal.Color); } handle.SetTransform(Matrix3.Identity); diff --git a/Content.Server/Chunking/ChunkingSystem.cs b/Content.Server/Chunking/ChunkingSystem.cs index e4775439cb..9bc1ab0a3b 100644 --- a/Content.Server/Chunking/ChunkingSystem.cs +++ b/Content.Server/Chunking/ChunkingSystem.cs @@ -8,6 +8,7 @@ using Robust.Shared.Map; using Robust.Shared.Map.Components; using Robust.Shared.Player; using Robust.Shared.Utility; +using ChunkIndicesEnumerator = Robust.Shared.Map.Enumerators.ChunkIndicesEnumerator; namespace Content.Shared.Chunking; diff --git a/Content.Server/Decals/DecalSystem.cs b/Content.Server/Decals/DecalSystem.cs index 5bfd91486c..ad225afe22 100644 --- a/Content.Server/Decals/DecalSystem.cs +++ b/Content.Server/Decals/DecalSystem.cs @@ -21,6 +21,7 @@ using Robust.Shared.Threading; using Robust.Shared.Timing; using Robust.Shared.Utility; using static Content.Shared.Decals.DecalGridComponent; +using ChunkIndicesEnumerator = Robust.Shared.Map.Enumerators.ChunkIndicesEnumerator; namespace Content.Server.Decals { diff --git a/Content.Server/Parallax/BiomeSystem.cs b/Content.Server/Parallax/BiomeSystem.cs index 0b8c7a4d82..c4c2300870 100644 --- a/Content.Server/Parallax/BiomeSystem.cs +++ b/Content.Server/Parallax/BiomeSystem.cs @@ -29,6 +29,7 @@ using Robust.Shared.Prototypes; using Robust.Shared.Random; using Robust.Shared.Threading; using Robust.Shared.Utility; +using ChunkIndicesEnumerator = Robust.Shared.Map.Enumerators.ChunkIndicesEnumerator; namespace Content.Server.Parallax; diff --git a/Content.Shared/Decals/Decal.cs b/Content.Shared/Decals/Decal.cs index 56ecc829e7..cb6f944c65 100644 --- a/Content.Shared/Decals/Decal.cs +++ b/Content.Shared/Decals/Decal.cs @@ -8,7 +8,7 @@ namespace Content.Shared.Decals public sealed partial class Decal { // if these are made not-readonly, then decal grid state handling needs to be updated to clone decals. - [DataField("coordinates")] public Vector2 Coordinates = Vector2.Zero; + [DataField("coordinates")] public Vector2 Coordinates = Vector2.Zero; [DataField("id")] public string Id = string.Empty; [DataField("color")] public Color? Color; [DataField("angle")] public Angle Angle = Angle.Zero; diff --git a/Content.Shared/Decals/DecalGridComponent.cs b/Content.Shared/Decals/DecalGridComponent.cs index 140eb5bafc..8ac05cb280 100644 --- a/Content.Shared/Decals/DecalGridComponent.cs +++ b/Content.Shared/Decals/DecalGridComponent.cs @@ -12,7 +12,8 @@ namespace Content.Shared.Decals [NetworkedComponent] public sealed partial class DecalGridComponent : Component { - [DataField("chunkCollection", serverOnly: true)] + [Access(Other = AccessPermissions.ReadExecute)] + [DataField(serverOnly: true)] public DecalGridChunkCollection ChunkCollection = new(new ()); /// @@ -25,10 +26,6 @@ namespace Content.Shared.Decals /// public GameTick ForceTick { get; set; } - // client-side data. I CBF creating a separate client-side comp for this. The server can survive with some empty dictionaries. - public readonly Dictionary DecalZIndexIndex = new(); - public readonly SortedDictionary> DecalRenderIndex = new(); - [DataDefinition] [Serializable, NetSerializable] public sealed partial class DecalChunk diff --git a/Content.Shared/Decals/SharedDecalSystem.cs b/Content.Shared/Decals/SharedDecalSystem.cs index 02f73bdacb..76fa9d64db 100644 --- a/Content.Shared/Decals/SharedDecalSystem.cs +++ b/Content.Shared/Decals/SharedDecalSystem.cs @@ -122,39 +122,6 @@ namespace Content.Shared.Decals } } - // TODO: Pretty sure paul was moving this somewhere but just so people know - public struct ChunkIndicesEnumerator - { - private Vector2i _chunkLB; - private Vector2i _chunkRT; - - private int _xIndex; - private int _yIndex; - - public ChunkIndicesEnumerator(Box2 localAABB, int chunkSize) - { - _chunkLB = new Vector2i((int)Math.Floor(localAABB.Left / chunkSize), (int)Math.Floor(localAABB.Bottom / chunkSize)); - _chunkRT = new Vector2i((int)Math.Floor(localAABB.Right / chunkSize), (int)Math.Floor(localAABB.Top / chunkSize)); - - _xIndex = _chunkLB.X; - _yIndex = _chunkLB.Y; - } - - public bool MoveNext([NotNullWhen(true)] out Vector2i? indices) - { - if (_yIndex > _chunkRT.Y) - { - _yIndex = _chunkLB.Y; - _xIndex += 1; - } - - indices = new Vector2i(_xIndex, _yIndex); - _yIndex += 1; - - return _xIndex <= _chunkRT.X; - } - } - /// /// Sent by clients to request that a decal is placed on the server. ///