Changes to reduce allocations in the main loop.
Targeted some methods that generated allocated a lot of memory in the main game loop: - Actor.Render - Changed LINQ calls into equivalent loops. No allocation for delegates. - Animation.Render - Returned an array rather than a yield expression. The array uses less memory than the complier generated enumerable. - FrozenActor and FrozenUnderFog - Materialize the footprint into an array: The enumerable is not-trivial to evaluate is and evaluated many times inside the Tick function. The memory needed is minimal. Changed LINQ into equivalent loops to prevent delegate allocation. Should result in overall much faster calls. - Widget.GetEventBounds - Changed LINQ calls into equivalent loops. - MobileInfo.CanEnterCell - Changed LINQ calls into equivalent loops. Don't materialize list of blocking actors every time, instead enumerate them and only when they need to be checked. - FrozenUnderFog.TickRender - Generate the renderables lazily and also remove a no-op Select call.
This commit is contained in:
@@ -81,9 +81,6 @@ namespace OpenRA
|
|||||||
health = Exts.Lazy(() => TraitOrDefault<Health>());
|
health = Exts.Lazy(() => TraitOrDefault<Health>());
|
||||||
effectiveOwner = Exts.Lazy(() => TraitOrDefault<IEffectiveOwner>());
|
effectiveOwner = Exts.Lazy(() => TraitOrDefault<IEffectiveOwner>());
|
||||||
|
|
||||||
applyIRender = (x, wr) => x.Render(this, wr);
|
|
||||||
applyRenderModifier = (m, p, wr) => p.ModifyRender(this, wr, m);
|
|
||||||
|
|
||||||
Bounds = Exts.Lazy(() =>
|
Bounds = Exts.Lazy(() =>
|
||||||
{
|
{
|
||||||
var si = Info.Traits.GetOrDefault<SelectableInfo>();
|
var si = Info.Traits.GetOrDefault<SelectableInfo>();
|
||||||
@@ -112,14 +109,19 @@ namespace OpenRA
|
|||||||
get { return currentActivity == null; }
|
get { return currentActivity == null; }
|
||||||
}
|
}
|
||||||
|
|
||||||
// note: these delegates are cached to avoid massive allocation.
|
|
||||||
Func<IRender, WorldRenderer, IEnumerable<IRenderable>> applyIRender;
|
|
||||||
Func<IEnumerable<IRenderable>, IRenderModifier, WorldRenderer, IEnumerable<IRenderable>> applyRenderModifier;
|
|
||||||
public IEnumerable<IRenderable> Render(WorldRenderer wr)
|
public IEnumerable<IRenderable> Render(WorldRenderer wr)
|
||||||
{
|
{
|
||||||
var mods = TraitsImplementing<IRenderModifier>();
|
var renderables = Renderables(wr);
|
||||||
var sprites = TraitsImplementing<IRender>().SelectMany(x => applyIRender(x, wr));
|
foreach (var modifier in TraitsImplementing<IRenderModifier>())
|
||||||
return mods.Aggregate(sprites, (m, p) => applyRenderModifier(m, p, wr));
|
renderables = modifier.ModifyRender(this, wr, renderables);
|
||||||
|
return renderables;
|
||||||
|
}
|
||||||
|
|
||||||
|
IEnumerable<IRenderable> Renderables(WorldRenderer wr)
|
||||||
|
{
|
||||||
|
foreach (var render in TraitsImplementing<IRender>())
|
||||||
|
foreach (var renderable in render.Render(this, wr))
|
||||||
|
yield return renderable;
|
||||||
}
|
}
|
||||||
|
|
||||||
public bool IsInWorld { get; internal set; }
|
public bool IsInWorld { get; internal set; }
|
||||||
|
|||||||
@@ -49,13 +49,16 @@ namespace OpenRA.Graphics
|
|||||||
|
|
||||||
public IEnumerable<IRenderable> Render(WPos pos, WVec offset, int zOffset, PaletteReference palette, float scale)
|
public IEnumerable<IRenderable> Render(WPos pos, WVec offset, int zOffset, PaletteReference palette, float scale)
|
||||||
{
|
{
|
||||||
|
var imageRenderable = new SpriteRenderable(Image, pos, offset, CurrentSequence.ZOffset + zOffset, palette, scale, IsDecoration);
|
||||||
|
|
||||||
if (CurrentSequence.ShadowStart >= 0)
|
if (CurrentSequence.ShadowStart >= 0)
|
||||||
{
|
{
|
||||||
var shadow = CurrentSequence.GetShadow(CurrentFrame, facingFunc());
|
var shadow = CurrentSequence.GetShadow(CurrentFrame, facingFunc());
|
||||||
yield return new SpriteRenderable(shadow, pos, offset, CurrentSequence.ShadowZOffset + zOffset, palette, scale, true);
|
var shadowRenderable = new SpriteRenderable(shadow, pos, offset, CurrentSequence.ShadowZOffset + zOffset, palette, scale, true);
|
||||||
|
return new IRenderable[] { shadowRenderable, imageRenderable };
|
||||||
}
|
}
|
||||||
|
|
||||||
yield return new SpriteRenderable(Image, pos, offset, CurrentSequence.ZOffset + zOffset, palette, scale, IsDecoration);
|
return new IRenderable[] { imageRenderable };
|
||||||
}
|
}
|
||||||
|
|
||||||
public IEnumerable<IRenderable> Render(WPos pos, PaletteReference palette)
|
public IEnumerable<IRenderable> Render(WPos pos, PaletteReference palette)
|
||||||
|
|||||||
@@ -22,7 +22,7 @@ namespace OpenRA.Traits
|
|||||||
|
|
||||||
public class FrozenActor
|
public class FrozenActor
|
||||||
{
|
{
|
||||||
public readonly IEnumerable<CPos> Footprint;
|
public readonly CPos[] Footprint;
|
||||||
public readonly WPos CenterPosition;
|
public readonly WPos CenterPosition;
|
||||||
public readonly Rectangle Bounds;
|
public readonly Rectangle Bounds;
|
||||||
readonly Actor actor;
|
readonly Actor actor;
|
||||||
@@ -41,7 +41,7 @@ namespace OpenRA.Traits
|
|||||||
public FrozenActor(Actor self, IEnumerable<CPos> footprint)
|
public FrozenActor(Actor self, IEnumerable<CPos> footprint)
|
||||||
{
|
{
|
||||||
actor = self;
|
actor = self;
|
||||||
Footprint = footprint;
|
Footprint = footprint.ToArray();
|
||||||
CenterPosition = self.CenterPosition;
|
CenterPosition = self.CenterPosition;
|
||||||
Bounds = self.Bounds.Value;
|
Bounds = self.Bounds.Value;
|
||||||
}
|
}
|
||||||
@@ -54,7 +54,13 @@ namespace OpenRA.Traits
|
|||||||
int flashTicks;
|
int flashTicks;
|
||||||
public void Tick(World world, Shroud shroud)
|
public void Tick(World world, Shroud shroud)
|
||||||
{
|
{
|
||||||
Visible = !Footprint.Any(c => shroud.IsVisible(c));
|
Visible = false;
|
||||||
|
foreach (var pos in Footprint)
|
||||||
|
if (shroud.IsVisible(pos))
|
||||||
|
{
|
||||||
|
Visible = true;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
if (flashTicks > 0)
|
if (flashTicks > 0)
|
||||||
flashTicks--;
|
flashTicks--;
|
||||||
|
|||||||
@@ -237,10 +237,11 @@ namespace OpenRA.Widgets
|
|||||||
|
|
||||||
public virtual Rectangle GetEventBounds()
|
public virtual Rectangle GetEventBounds()
|
||||||
{
|
{
|
||||||
return Children
|
var bounds = EventBounds;
|
||||||
.Where(c => c.IsVisible())
|
foreach (var child in Children)
|
||||||
.Select(c => c.GetEventBounds())
|
if (child.IsVisible())
|
||||||
.Aggregate(EventBounds, Rectangle.Union);
|
bounds = Rectangle.Union(bounds, child.GetEventBounds());
|
||||||
|
return bounds;
|
||||||
}
|
}
|
||||||
|
|
||||||
public bool HasMouseFocus { get { return Ui.MouseFocusWidget == this; } }
|
public bool HasMouseFocus { get { return Ui.MouseFocusWidget == this; } }
|
||||||
|
|||||||
@@ -30,7 +30,7 @@ namespace OpenRA.Mods.RA
|
|||||||
[Sync] public int VisibilityHash;
|
[Sync] public int VisibilityHash;
|
||||||
|
|
||||||
bool initialized, startsRevealed;
|
bool initialized, startsRevealed;
|
||||||
IEnumerable<CPos> footprint;
|
readonly CPos[] footprint;
|
||||||
Lazy<IToolTip> tooltip;
|
Lazy<IToolTip> tooltip;
|
||||||
Lazy<Health> health;
|
Lazy<Health> health;
|
||||||
|
|
||||||
@@ -41,7 +41,7 @@ namespace OpenRA.Mods.RA
|
|||||||
{
|
{
|
||||||
// Spawned actors (e.g. building husks) shouldn't be revealed
|
// Spawned actors (e.g. building husks) shouldn't be revealed
|
||||||
startsRevealed = info.StartsRevealed && !init.Contains<ParentActorInit>();
|
startsRevealed = info.StartsRevealed && !init.Contains<ParentActorInit>();
|
||||||
footprint = FootprintUtils.Tiles(init.self);
|
footprint = FootprintUtils.Tiles(init.self).ToArray();
|
||||||
tooltip = Exts.Lazy(() => init.self.TraitsImplementing<IToolTip>().FirstOrDefault());
|
tooltip = Exts.Lazy(() => init.self.TraitsImplementing<IToolTip>().FirstOrDefault());
|
||||||
tooltip = Exts.Lazy(() => init.self.TraitsImplementing<IToolTip>().FirstOrDefault());
|
tooltip = Exts.Lazy(() => init.self.TraitsImplementing<IToolTip>().FirstOrDefault());
|
||||||
health = Exts.Lazy(() => init.self.TraitOrDefault<Health>());
|
health = Exts.Lazy(() => init.self.TraitOrDefault<Health>());
|
||||||
@@ -63,8 +63,15 @@ namespace OpenRA.Mods.RA
|
|||||||
VisibilityHash = 0;
|
VisibilityHash = 0;
|
||||||
foreach (var p in self.World.Players)
|
foreach (var p in self.World.Players)
|
||||||
{
|
{
|
||||||
visible[p] = footprint.Any(c => p.Shroud.IsVisible(c));
|
var isVisible = false;
|
||||||
if (visible[p])
|
foreach (var pos in footprint)
|
||||||
|
if (p.Shroud.IsVisible(pos))
|
||||||
|
{
|
||||||
|
isVisible = true;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
visible[p] = isVisible;
|
||||||
|
if (isVisible)
|
||||||
VisibilityHash += p.ClientIndex;
|
VisibilityHash += p.ClientIndex;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -106,11 +113,15 @@ namespace OpenRA.Mods.RA
|
|||||||
if (self.Destroyed || !initialized || !visible.Any(v => v.Value))
|
if (self.Destroyed || !initialized || !visible.Any(v => v.Value))
|
||||||
return;
|
return;
|
||||||
|
|
||||||
// Force a copy of the underlying data
|
IRenderable[] renderables = null;
|
||||||
var renderables = self.Render(wr).Select(rr => rr).ToArray();
|
|
||||||
foreach (var player in self.World.Players)
|
foreach (var player in self.World.Players)
|
||||||
if (visible[player])
|
if (visible[player])
|
||||||
|
{
|
||||||
|
// Lazily generate a copy of the underlying data.
|
||||||
|
if (renderables == null)
|
||||||
|
renderables = self.Render(wr).ToArray();
|
||||||
frozen[player].Renderables = renderables;
|
frozen[player].Renderables = renderables;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public IEnumerable<IRenderable> ModifyRender(Actor self, WorldRenderer wr, IEnumerable<IRenderable> r)
|
public IEnumerable<IRenderable> ModifyRender(Actor self, WorldRenderer wr, IEnumerable<IRenderable> r)
|
||||||
|
|||||||
@@ -121,21 +121,24 @@ namespace OpenRA.Mods.RA.Move
|
|||||||
if (SharesCell && world.ActorMap.HasFreeSubCell(cell))
|
if (SharesCell && world.ActorMap.HasFreeSubCell(cell))
|
||||||
return true;
|
return true;
|
||||||
|
|
||||||
var blockingActors = world.ActorMap.GetUnitsAt(cell)
|
if (checkTransientActors)
|
||||||
.Where(x => x != ignoreActor)
|
|
||||||
// Neutral/enemy units are blockers. Allied units that are moving are not blockers.
|
|
||||||
.Where(x => blockedByMovers || (self == null || self.Owner.Stances[x.Owner] != Stance.Ally || !IsMovingInMyDirection(self, x)))
|
|
||||||
.ToList();
|
|
||||||
|
|
||||||
if (checkTransientActors && blockingActors.Count > 0)
|
|
||||||
{
|
{
|
||||||
// Non-sharable unit can enter a cell with shareable units only if it can crush all of them
|
bool canIgnoreMovingAllies = self != null && !blockedByMovers;
|
||||||
if (self == null || Crushes == null)
|
bool needsCellExclusively = self == null || Crushes == null;
|
||||||
return false;
|
foreach(var a in world.ActorMap.GetUnitsAt(cell))
|
||||||
|
{
|
||||||
|
if (a == ignoreActor) continue;
|
||||||
|
|
||||||
if (blockingActors.Any(a => !(a.HasTrait<ICrushable>() &&
|
// Neutral/enemy units are blockers. Allied units that are moving are not blockers.
|
||||||
a.TraitsImplementing<ICrushable>().Any(b => b.CrushableBy(Crushes, self.Owner)))))
|
if (canIgnoreMovingAllies && self.Owner.Stances[a.Owner] == Stance.Ally && IsMovingInMyDirection(self, a)) continue;
|
||||||
return false;
|
|
||||||
|
// Non-sharable unit can enter a cell with shareable units only if it can crush all of them.
|
||||||
|
if (needsCellExclusively) return false;
|
||||||
|
if (!a.HasTrait<ICrushable>()) return false;
|
||||||
|
foreach (var crushable in a.TraitsImplementing<ICrushable>())
|
||||||
|
if (!crushable.CrushableBy(Crushes, self.Owner))
|
||||||
|
return false;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return true;
|
return true;
|
||||||
|
|||||||
Reference in New Issue
Block a user