Merge pull request #12599 from RoosterDragon/allocs
Reduce ongoing allocations
This commit is contained in:
@@ -166,8 +166,10 @@ namespace OpenRA.Network
|
|||||||
|
|
||||||
struct TypeInfo
|
struct TypeInfo
|
||||||
{
|
{
|
||||||
static ParameterExpression syncParam = Expression.Parameter(typeof(ISync), "sync");
|
static readonly ParameterExpression SyncParam = Expression.Parameter(typeof(ISync), "sync");
|
||||||
static ConstantExpression nullString = Expression.Constant(null, typeof(string));
|
static readonly ConstantExpression NullString = Expression.Constant(null, typeof(string));
|
||||||
|
static readonly ConstantExpression TrueString = Expression.Constant(bool.TrueString, typeof(string));
|
||||||
|
static readonly ConstantExpression FalseString = Expression.Constant(bool.FalseString, typeof(string));
|
||||||
|
|
||||||
public readonly Func<ISync, object>[] SerializableCopyOfMemberFunctions;
|
public readonly Func<ISync, object>[] SerializableCopyOfMemberFunctions;
|
||||||
public readonly string[] Names;
|
public readonly string[] Names;
|
||||||
@@ -184,7 +186,7 @@ namespace OpenRA.Network
|
|||||||
"Properties using the Sync attribute must be readable and must not use index parameters.\n" +
|
"Properties using the Sync attribute must be readable and must not use index parameters.\n" +
|
||||||
"Invalid Property: " + prop.DeclaringType.FullName + "." + prop.Name);
|
"Invalid Property: " + prop.DeclaringType.FullName + "." + prop.Name);
|
||||||
|
|
||||||
var sync = Expression.Convert(syncParam, type);
|
var sync = Expression.Convert(SyncParam, type);
|
||||||
SerializableCopyOfMemberFunctions = fields
|
SerializableCopyOfMemberFunctions = fields
|
||||||
.Select(fi => SerializableCopyOfMember(Expression.Field(sync, fi), fi.FieldType, fi.Name))
|
.Select(fi => SerializableCopyOfMember(Expression.Field(sync, fi), fi.FieldType, fi.Name))
|
||||||
.Concat(properties.Select(pi => SerializableCopyOfMember(Expression.Property(sync, pi), pi.PropertyType, pi.Name)))
|
.Concat(properties.Select(pi => SerializableCopyOfMember(Expression.Property(sync, pi), pi.PropertyType, pi.Name)))
|
||||||
@@ -203,8 +205,16 @@ namespace OpenRA.Network
|
|||||||
// just box a copy of the current value into an object. This is faster than calling ToString. We
|
// just box a copy of the current value into an object. This is faster than calling ToString. We
|
||||||
// can call ToString later when we generate the report. Most of the time, the sync report is never
|
// can call ToString later when we generate the report. Most of the time, the sync report is never
|
||||||
// generated so we successfully avoid the overhead to calling ToString.
|
// generated so we successfully avoid the overhead to calling ToString.
|
||||||
|
if (memberType == typeof(bool))
|
||||||
|
{
|
||||||
|
// PERF: If the member is a Boolean, we can also avoid the allocation caused by boxing it.
|
||||||
|
// Instead, we can just return the resulting strings directly.
|
||||||
|
var getBoolString = Expression.Condition(getMember, TrueString, FalseString);
|
||||||
|
return Expression.Lambda<Func<ISync, string>>(getBoolString, name, new[] { SyncParam }).Compile();
|
||||||
|
}
|
||||||
|
|
||||||
var boxedCopy = Expression.Convert(getMember, typeof(object));
|
var boxedCopy = Expression.Convert(getMember, typeof(object));
|
||||||
return Expression.Lambda<Func<ISync, object>>(boxedCopy, name, new[] { syncParam }).Compile();
|
return Expression.Lambda<Func<ISync, object>>(boxedCopy, name, new[] { SyncParam }).Compile();
|
||||||
}
|
}
|
||||||
|
|
||||||
// For reference types, we have to call ToString right away to get a snapshot of the value. We cannot
|
// For reference types, we have to call ToString right away to get a snapshot of the value. We cannot
|
||||||
@@ -231,10 +241,10 @@ namespace OpenRA.Network
|
|||||||
var member = Expression.Block(new[] { memberVariable }, assignMemberVariable);
|
var member = Expression.Block(new[] { memberVariable }, assignMemberVariable);
|
||||||
getString = Expression.Call(member, toString);
|
getString = Expression.Call(member, toString);
|
||||||
var nullMember = Expression.Constant(null, memberType);
|
var nullMember = Expression.Constant(null, memberType);
|
||||||
getString = Expression.Condition(Expression.Equal(member, nullMember), nullString, getString);
|
getString = Expression.Condition(Expression.Equal(member, nullMember), NullString, getString);
|
||||||
}
|
}
|
||||||
|
|
||||||
return Expression.Lambda<Func<ISync, string>>(getString, name, new[] { syncParam }).Compile();
|
return Expression.Lambda<Func<ISync, string>>(getString, name, new[] { SyncParam }).Compile();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -52,7 +52,7 @@ namespace OpenRA.Mods.Common.Traits
|
|||||||
|
|
||||||
public class AutoTarget : ConditionalTrait<AutoTargetInfo>, INotifyIdle, INotifyDamage, ITick, IResolveOrder, ISync
|
public class AutoTarget : ConditionalTrait<AutoTargetInfo>, INotifyIdle, INotifyDamage, ITick, IResolveOrder, ISync
|
||||||
{
|
{
|
||||||
readonly AttackBase[] attackBases;
|
readonly IEnumerable<AttackBase> activeAttackBases;
|
||||||
readonly AttackFollow[] attackFollows;
|
readonly AttackFollow[] attackFollows;
|
||||||
[Sync] int nextScanTime = 0;
|
[Sync] int nextScanTime = 0;
|
||||||
|
|
||||||
@@ -67,7 +67,7 @@ namespace OpenRA.Mods.Common.Traits
|
|||||||
: base(info)
|
: base(info)
|
||||||
{
|
{
|
||||||
var self = init.Self;
|
var self = init.Self;
|
||||||
attackBases = self.TraitsImplementing<AttackBase>().ToArray();
|
activeAttackBases = self.TraitsImplementing<AttackBase>().ToArray().Where(Exts.IsTraitEnabled);
|
||||||
|
|
||||||
if (init.Contains<StanceInit>())
|
if (init.Contains<StanceInit>())
|
||||||
Stance = init.Get<StanceInit, UnitStance>();
|
Stance = init.Get<StanceInit, UnitStance>();
|
||||||
@@ -105,7 +105,8 @@ namespace OpenRA.Mods.Common.Traits
|
|||||||
}
|
}
|
||||||
|
|
||||||
// not a lot we can do about things we can't hurt... although maybe we should automatically run away?
|
// not a lot we can do about things we can't hurt... although maybe we should automatically run away?
|
||||||
if (attackBases.All(a => a.IsTraitDisabled || !a.HasAnyValidWeapons(Target.FromActor(attacker))))
|
var attackerAsTarget = Target.FromActor(attacker);
|
||||||
|
if (!activeAttackBases.Any(a => a.HasAnyValidWeapons(attackerAsTarget)))
|
||||||
return;
|
return;
|
||||||
|
|
||||||
// don't retaliate against own units force-firing on us. It's usually not what the player wanted.
|
// don't retaliate against own units force-firing on us. It's usually not what the player wanted.
|
||||||
@@ -117,8 +118,9 @@ namespace OpenRA.Mods.Common.Traits
|
|||||||
return;
|
return;
|
||||||
|
|
||||||
Aggressor = attacker;
|
Aggressor = attacker;
|
||||||
var allowMove = Info.AllowMovement && Stance != UnitStance.Defend;
|
|
||||||
if (attackFollows.All(a => a.IsTraitDisabled || !a.IsReachableTarget(a.Target, allowMove)))
|
bool allowMove;
|
||||||
|
if (ShouldAttack(out allowMove))
|
||||||
Attack(self, Aggressor, allowMove);
|
Attack(self, Aggressor, allowMove);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -130,11 +132,23 @@ namespace OpenRA.Mods.Common.Traits
|
|||||||
if (Stance < UnitStance.Defend || !Info.TargetWhenIdle)
|
if (Stance < UnitStance.Defend || !Info.TargetWhenIdle)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
var allowMove = Info.AllowMovement && Stance != UnitStance.Defend;
|
bool allowMove;
|
||||||
if (attackFollows.All(a => a.IsTraitDisabled || !a.IsReachableTarget(a.Target, allowMove)))
|
if (ShouldAttack(out allowMove))
|
||||||
ScanAndAttack(self, allowMove);
|
ScanAndAttack(self, allowMove);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
bool ShouldAttack(out bool allowMove)
|
||||||
|
{
|
||||||
|
allowMove = Info.AllowMovement && Stance != UnitStance.Defend;
|
||||||
|
|
||||||
|
// PERF: Avoid LINQ.
|
||||||
|
foreach (var attackFollow in attackFollows)
|
||||||
|
if (!attackFollow.IsTraitDisabled && attackFollow.IsReachableTarget(attackFollow.Target, allowMove))
|
||||||
|
return false;
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
public void Tick(Actor self)
|
public void Tick(Actor self)
|
||||||
{
|
{
|
||||||
if (IsTraitDisabled)
|
if (IsTraitDisabled)
|
||||||
@@ -146,8 +160,7 @@ namespace OpenRA.Mods.Common.Traits
|
|||||||
|
|
||||||
public Actor ScanForTarget(Actor self, bool allowMove)
|
public Actor ScanForTarget(Actor self, bool allowMove)
|
||||||
{
|
{
|
||||||
var activeAttackBases = attackBases.Where(Exts.IsTraitEnabled);
|
if (nextScanTime <= 0 && activeAttackBases.Any())
|
||||||
if (activeAttackBases.Any() && nextScanTime <= 0)
|
|
||||||
{
|
{
|
||||||
nextScanTime = self.World.SharedRandom.Next(Info.MinimumScanTimeInterval, Info.MaximumScanTimeInterval);
|
nextScanTime = self.World.SharedRandom.Next(Info.MinimumScanTimeInterval, Info.MaximumScanTimeInterval);
|
||||||
|
|
||||||
@@ -160,8 +173,6 @@ namespace OpenRA.Mods.Common.Traits
|
|||||||
var range = Info.ScanRadius > 0 ? WDist.FromCells(Info.ScanRadius) : ab.GetMaximumRange();
|
var range = Info.ScanRadius > 0 ? WDist.FromCells(Info.ScanRadius) : ab.GetMaximumRange();
|
||||||
return ChooseTarget(self, ab, attackStances, range, allowMove);
|
return ChooseTarget(self, ab, attackStances, range, allowMove);
|
||||||
}
|
}
|
||||||
|
|
||||||
continue;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -181,7 +192,6 @@ namespace OpenRA.Mods.Common.Traits
|
|||||||
var target = Target.FromActor(targetActor);
|
var target = Target.FromActor(targetActor);
|
||||||
self.SetTargetLine(target, Color.Red, false);
|
self.SetTargetLine(target, Color.Red, false);
|
||||||
|
|
||||||
var activeAttackBases = attackBases.Where(Exts.IsTraitEnabled);
|
|
||||||
foreach (var ab in activeAttackBases)
|
foreach (var ab in activeAttackBases)
|
||||||
ab.AttackTarget(target, false, allowMove);
|
ab.AttackTarget(target, false, allowMove);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -92,8 +92,8 @@ namespace OpenRA.Mods.Common.Traits
|
|||||||
|
|
||||||
foreach (var kvp in palettes)
|
foreach (var kvp in palettes)
|
||||||
{
|
{
|
||||||
if ((info.Palettes.Count > 0 && !info.Palettes.Any(kvp.Key.StartsWith))
|
if ((info.Palettes.Count > 0 && !AnyPaletteNameStartsWith(info.Palettes, kvp.Key))
|
||||||
|| (info.ExcludePalettes.Count > 0 && info.ExcludePalettes.Any(kvp.Key.StartsWith)))
|
|| (info.ExcludePalettes.Count > 0 && AnyPaletteNameStartsWith(info.ExcludePalettes, kvp.Key)))
|
||||||
continue;
|
continue;
|
||||||
|
|
||||||
var palette = kvp.Value;
|
var palette = kvp.Value;
|
||||||
@@ -105,5 +105,15 @@ namespace OpenRA.Mods.Common.Traits
|
|||||||
palette[info.RotationBase + i] = rotationBuffer[i];
|
palette[info.RotationBase + i] = rotationBuffer[i];
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static bool AnyPaletteNameStartsWith(HashSet<string> names, string prefix)
|
||||||
|
{
|
||||||
|
// PERF: Avoid LINQ.
|
||||||
|
foreach (var name in names)
|
||||||
|
if (name.StartsWith(prefix))
|
||||||
|
return true;
|
||||||
|
|
||||||
|
return false;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -9,6 +9,7 @@
|
|||||||
*/
|
*/
|
||||||
#endregion
|
#endregion
|
||||||
|
|
||||||
|
using System;
|
||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
using System.Drawing;
|
using System.Drawing;
|
||||||
using System.Linq;
|
using System.Linq;
|
||||||
@@ -29,6 +30,9 @@ namespace OpenRA.Mods.Common.Traits.Radar
|
|||||||
readonly AppearsOnRadarInfo info;
|
readonly AppearsOnRadarInfo info;
|
||||||
IRadarColorModifier modifier;
|
IRadarColorModifier modifier;
|
||||||
|
|
||||||
|
Color currentColor = Color.Transparent;
|
||||||
|
Func<Pair<CPos, SubCell>, Pair<CPos, Color>> cellToSignature;
|
||||||
|
|
||||||
public AppearsOnRadar(AppearsOnRadarInfo info)
|
public AppearsOnRadar(AppearsOnRadarInfo info)
|
||||||
{
|
{
|
||||||
this.info = info;
|
this.info = info;
|
||||||
@@ -48,7 +52,14 @@ namespace OpenRA.Mods.Common.Traits.Radar
|
|||||||
if (info.UseLocation)
|
if (info.UseLocation)
|
||||||
return new[] { Pair.New(self.Location, color) };
|
return new[] { Pair.New(self.Location, color) };
|
||||||
|
|
||||||
return self.OccupiesSpace.OccupiedCells().Select(c => Pair.New(c.First, color));
|
// PERF: Cache cellToSignature delegate to avoid allocations as color does not change often.
|
||||||
|
if (currentColor != color)
|
||||||
|
{
|
||||||
|
currentColor = color;
|
||||||
|
cellToSignature = c => Pair.New(c.First, color);
|
||||||
|
}
|
||||||
|
|
||||||
|
return self.OccupiesSpace.OccupiedCells().Select(cellToSignature);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -41,7 +41,7 @@ namespace OpenRA.Mods.Common.Traits.Render
|
|||||||
public int[] SelectionBoxBounds { get { return VisualBounds; } }
|
public int[] SelectionBoxBounds { get { return VisualBounds; } }
|
||||||
}
|
}
|
||||||
|
|
||||||
public class SelectionDecorations : IRenderAboveShroud, ITick
|
public class SelectionDecorations : IRenderAboveShroud, INotifyCreated, ITick
|
||||||
{
|
{
|
||||||
// depends on the order of pips in TraitsInterfaces.cs!
|
// depends on the order of pips in TraitsInterfaces.cs!
|
||||||
static readonly string[] PipStrings = { "pip-empty", "pip-green", "pip-yellow", "pip-red", "pip-gray", "pip-blue", "pip-ammo", "pip-ammoempty" };
|
static readonly string[] PipStrings = { "pip-empty", "pip-green", "pip-yellow", "pip-red", "pip-gray", "pip-blue", "pip-ammo", "pip-ammoempty" };
|
||||||
@@ -49,6 +49,7 @@ namespace OpenRA.Mods.Common.Traits.Render
|
|||||||
public readonly SelectionDecorationsInfo Info;
|
public readonly SelectionDecorationsInfo Info;
|
||||||
|
|
||||||
readonly Animation pipImages;
|
readonly Animation pipImages;
|
||||||
|
IPips[] pipSources;
|
||||||
|
|
||||||
public SelectionDecorations(Actor self, SelectionDecorationsInfo info)
|
public SelectionDecorations(Actor self, SelectionDecorationsInfo info)
|
||||||
{
|
{
|
||||||
@@ -57,6 +58,11 @@ namespace OpenRA.Mods.Common.Traits.Render
|
|||||||
pipImages = new Animation(self.World, Info.Image);
|
pipImages = new Animation(self.World, Info.Image);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void INotifyCreated.Created(Actor self)
|
||||||
|
{
|
||||||
|
pipSources = self.TraitsImplementing<IPips>().ToArray();
|
||||||
|
}
|
||||||
|
|
||||||
IEnumerable<WPos> ActivityTargetPath(Actor self)
|
IEnumerable<WPos> ActivityTargetPath(Actor self)
|
||||||
{
|
{
|
||||||
if (!self.IsInWorld || self.IsDead)
|
if (!self.IsInWorld || self.IsDead)
|
||||||
@@ -76,8 +82,13 @@ namespace OpenRA.Mods.Common.Traits.Render
|
|||||||
IEnumerable<IRenderable> IRenderAboveShroud.RenderAboveShroud(Actor self, WorldRenderer wr)
|
IEnumerable<IRenderable> IRenderAboveShroud.RenderAboveShroud(Actor self, WorldRenderer wr)
|
||||||
{
|
{
|
||||||
if (self.World.FogObscures(self))
|
if (self.World.FogObscures(self))
|
||||||
yield break;
|
return Enumerable.Empty<IRenderable>();
|
||||||
|
|
||||||
|
return DrawDecorations(self, wr);
|
||||||
|
}
|
||||||
|
|
||||||
|
IEnumerable<IRenderable> DrawDecorations(Actor self, WorldRenderer wr)
|
||||||
|
{
|
||||||
var selected = self.World.Selection.Contains(self);
|
var selected = self.World.Selection.Contains(self);
|
||||||
var regularWorld = self.World.Type == WorldType.Regular;
|
var regularWorld = self.World.Type == WorldType.Regular;
|
||||||
var statusBars = Game.Settings.Game.StatusBars;
|
var statusBars = Game.Settings.Game.StatusBars;
|
||||||
@@ -108,21 +119,25 @@ namespace OpenRA.Mods.Common.Traits.Render
|
|||||||
if (self.World.LocalPlayer != null && self.World.LocalPlayer.PlayerActor.Trait<DeveloperMode>().PathDebug)
|
if (self.World.LocalPlayer != null && self.World.LocalPlayer.PlayerActor.Trait<DeveloperMode>().PathDebug)
|
||||||
yield return new TargetLineRenderable(ActivityTargetPath(self), Color.Green);
|
yield return new TargetLineRenderable(ActivityTargetPath(self), Color.Green);
|
||||||
|
|
||||||
|
foreach (var r in DrawPips(self, wr))
|
||||||
|
yield return r;
|
||||||
|
}
|
||||||
|
|
||||||
|
IEnumerable<IRenderable> DrawPips(Actor self, WorldRenderer wr)
|
||||||
|
{
|
||||||
|
if (pipSources.Length == 0)
|
||||||
|
return Enumerable.Empty<IRenderable>();
|
||||||
|
|
||||||
var b = self.VisualBounds;
|
var b = self.VisualBounds;
|
||||||
var pos = wr.ScreenPxPosition(self.CenterPosition);
|
var pos = wr.ScreenPxPosition(self.CenterPosition);
|
||||||
var bl = wr.Viewport.WorldToViewPx(pos + new int2(b.Left, b.Bottom));
|
var bl = wr.Viewport.WorldToViewPx(pos + new int2(b.Left, b.Bottom));
|
||||||
var pal = wr.Palette(Info.Palette);
|
var pal = wr.Palette(Info.Palette);
|
||||||
|
|
||||||
foreach (var r in DrawPips(self, wr, bl, pal))
|
return DrawPips(self, bl, pal);
|
||||||
yield return r;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
IEnumerable<IRenderable> DrawPips(Actor self, WorldRenderer wr, int2 basePosition, PaletteReference palette)
|
IEnumerable<IRenderable> DrawPips(Actor self, int2 basePosition, PaletteReference palette)
|
||||||
{
|
{
|
||||||
var pipSources = self.TraitsImplementing<IPips>();
|
|
||||||
if (!pipSources.Any())
|
|
||||||
yield break;
|
|
||||||
|
|
||||||
pipImages.PlayRepeating(PipStrings[0]);
|
pipImages.PlayRepeating(PipStrings[0]);
|
||||||
|
|
||||||
var pipSize = pipImages.Image.Size.XY.ToInt2();
|
var pipSize = pipImages.Image.Size.XY.ToInt2();
|
||||||
|
|||||||
Reference in New Issue
Block a user