diff --git a/OpenRA.Game/Actor.cs b/OpenRA.Game/Actor.cs index 8d1fe13d23..982b441d3c 100644 --- a/OpenRA.Game/Actor.cs +++ b/OpenRA.Game/Actor.cs @@ -123,6 +123,9 @@ namespace OpenRA readonly IEnumerable enabledTargetableWorldPositions; bool created; + IEnumerable renderables; + WorldRenderer lastWorldRenderer; + internal Actor(World world, string name, TypeDictionary initDict) { var duplicateInit = initDict.WithInterface().GroupBy(i => i.GetType()) @@ -287,11 +290,18 @@ namespace OpenRA public IEnumerable Render(WorldRenderer wr) { + if (lastWorldRenderer != wr) + { + // PERF: Cache the enumerable to reduce allocations. + lastWorldRenderer = wr; + renderables = Renderables(wr); + } + // PERF: Avoid LINQ. - var renderables = Renderables(wr); + var modifiedRenderables = renderables; foreach (var modifier in renderModifiers) - renderables = modifier.ModifyRender(this, wr, renderables); - return renderables; + modifiedRenderables = modifier.ModifyRender(this, wr, modifiedRenderables); + return modifiedRenderables; } IEnumerable Renderables(WorldRenderer wr) diff --git a/OpenRA.Mods.Common/Traits/AutoTarget.cs b/OpenRA.Mods.Common/Traits/AutoTarget.cs index 6e7d5e108c..abcbb04985 100644 --- a/OpenRA.Mods.Common/Traits/AutoTarget.cs +++ b/OpenRA.Mods.Common/Traits/AutoTarget.cs @@ -9,6 +9,7 @@ */ #endregion +using System; using System.Collections.Frozen; using System.Collections.Generic; using System.Linq; @@ -431,9 +432,16 @@ namespace OpenRA.Mods.Common.Traits // Make sure that we can actually fire on the actor var armaments = ab.ChooseArmamentsForTarget(target, false); if (!allowMove) - armaments = armaments.Where(arm => - target.IsInRange(self.CenterPosition, arm.MaxRange()) && - !target.IsInRange(self.CenterPosition, arm.Weapon.MinRange)); + { + // PERF: This lambda captures, contain it within a local function to prevent + // the compiler allocating the helper class at the top of the loop. + static Func IsInRange(Actor self, Target target) => + arm => + target.IsInRange(self.CenterPosition, arm.MaxRange()) && + !target.IsInRange(self.CenterPosition, arm.Weapon.MinRange); + + armaments = armaments.Where(IsInRange(self, target)); + } if (!armaments.Any()) continue; diff --git a/OpenRA.Mods.Common/Traits/Render/RenderSprites.cs b/OpenRA.Mods.Common/Traits/Render/RenderSprites.cs index 630ec96688..d454730615 100644 --- a/OpenRA.Mods.Common/Traits/Render/RenderSprites.cs +++ b/OpenRA.Mods.Common/Traits/Render/RenderSprites.cs @@ -143,6 +143,8 @@ namespace OpenRA.Mods.Common.Traits.Render public readonly RenderSpritesInfo Info; readonly string faction; readonly List anims = []; + readonly IEnumerable renderables; + bool shouldRefreshPalettes; string cachedImage; public static Func MakeFacingFunc(Actor self) @@ -158,6 +160,7 @@ namespace OpenRA.Mods.Common.Traits.Render { Info = info; faction = init.GetValue(init.Self.Owner.Faction.InternalName); + renderables = RenderAnimations(anims, init.Self); } public string GetImage(Actor self) @@ -170,6 +173,7 @@ namespace OpenRA.Mods.Common.Traits.Render public void UpdatePalette() { + shouldRefreshPalettes = true; foreach (var anim in anims) anim.OwnerChanged(); } @@ -178,18 +182,30 @@ namespace OpenRA.Mods.Common.Traits.Render public void OnEffectiveOwnerChanged(Actor self, Player oldEffectiveOwner, Player newEffectiveOwner) { UpdatePalette(); } public virtual IEnumerable Render(Actor self, WorldRenderer wr) + { + if (shouldRefreshPalettes) + { + shouldRefreshPalettes = false; + foreach (var a in anims) + { + if (a.PaletteReference == null) + { + var owner = self.EffectiveOwner != null && self.EffectiveOwner.Disguised ? self.EffectiveOwner.Owner : self.Owner; + a.CachePalette(wr, owner); + } + } + } + + return renderables; + } + + static IEnumerable RenderAnimations(List anims, Actor self) { foreach (var a in anims) { if (!a.IsVisible) continue; - if (a.PaletteReference == null) - { - var owner = self.EffectiveOwner != null && self.EffectiveOwner.Disguised ? self.EffectiveOwner.Owner : self.Owner; - a.CachePalette(wr, owner); - } - foreach (var r in a.Animation.Render(self, a.PaletteReference)) yield return r; } @@ -226,6 +242,7 @@ namespace OpenRA.Mods.Common.Traits.Render isPlayerPalette = Info.Palette == null; } + shouldRefreshPalettes = true; anims.Add(new AnimationWrapper(anim, palette, isPlayerPalette)); }