Merge pull request #5433 from RoosterDragon/main-loop-alloc

Reduce memory allocations in the main loop.
This commit is contained in:
Chris Forbes
2014-05-25 10:38:27 +12:00
6 changed files with 63 additions and 37 deletions

View File

@@ -81,9 +81,6 @@ namespace OpenRA
health = Exts.Lazy(() => TraitOrDefault<Health>());
effectiveOwner = Exts.Lazy(() => TraitOrDefault<IEffectiveOwner>());
applyIRender = (x, wr) => x.Render(this, wr);
applyRenderModifier = (m, p, wr) => p.ModifyRender(this, wr, m);
Bounds = Exts.Lazy(() =>
{
var si = Info.Traits.GetOrDefault<SelectableInfo>();
@@ -112,14 +109,19 @@ namespace OpenRA
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)
{
var mods = TraitsImplementing<IRenderModifier>();
var sprites = TraitsImplementing<IRender>().SelectMany(x => applyIRender(x, wr));
return mods.Aggregate(sprites, (m, p) => applyRenderModifier(m, p, wr));
var renderables = Renderables(wr);
foreach (var modifier in TraitsImplementing<IRenderModifier>())
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; }

View File

@@ -49,13 +49,16 @@ namespace OpenRA.Graphics
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)
{
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)

View File

@@ -22,7 +22,7 @@ namespace OpenRA.Traits
public class FrozenActor
{
public readonly IEnumerable<CPos> Footprint;
public readonly CPos[] Footprint;
public readonly WPos CenterPosition;
public readonly Rectangle Bounds;
readonly Actor actor;
@@ -41,7 +41,7 @@ namespace OpenRA.Traits
public FrozenActor(Actor self, IEnumerable<CPos> footprint)
{
actor = self;
Footprint = footprint;
Footprint = footprint.ToArray();
CenterPosition = self.CenterPosition;
Bounds = self.Bounds.Value;
}
@@ -54,7 +54,13 @@ namespace OpenRA.Traits
int flashTicks;
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)
flashTicks--;

View File

@@ -237,10 +237,11 @@ namespace OpenRA.Widgets
public virtual Rectangle GetEventBounds()
{
return Children
.Where(c => c.IsVisible())
.Select(c => c.GetEventBounds())
.Aggregate(EventBounds, Rectangle.Union);
var bounds = EventBounds;
foreach (var child in Children)
if (child.IsVisible())
bounds = Rectangle.Union(bounds, child.GetEventBounds());
return bounds;
}
public bool HasMouseFocus { get { return Ui.MouseFocusWidget == this; } }

View File

@@ -30,7 +30,7 @@ namespace OpenRA.Mods.RA
[Sync] public int VisibilityHash;
bool initialized, startsRevealed;
IEnumerable<CPos> footprint;
readonly CPos[] footprint;
Lazy<IToolTip> tooltip;
Lazy<Health> health;
@@ -41,7 +41,7 @@ namespace OpenRA.Mods.RA
{
// Spawned actors (e.g. building husks) shouldn't be revealed
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());
health = Exts.Lazy(() => init.self.TraitOrDefault<Health>());
@@ -63,8 +63,15 @@ namespace OpenRA.Mods.RA
VisibilityHash = 0;
foreach (var p in self.World.Players)
{
visible[p] = footprint.Any(c => p.Shroud.IsVisible(c));
if (visible[p])
var isVisible = false;
foreach (var pos in footprint)
if (p.Shroud.IsVisible(pos))
{
isVisible = true;
break;
}
visible[p] = isVisible;
if (isVisible)
VisibilityHash += p.ClientIndex;
}
@@ -106,11 +113,15 @@ namespace OpenRA.Mods.RA
if (self.Destroyed || !initialized || !visible.Any(v => v.Value))
return;
// Force a copy of the underlying data
var renderables = self.Render(wr).Select(rr => rr).ToArray();
IRenderable[] renderables = null;
foreach (var player in self.World.Players)
if (visible[player])
{
// Lazily generate a copy of the underlying data.
if (renderables == null)
renderables = self.Render(wr).ToArray();
frozen[player].Renderables = renderables;
}
}
public IEnumerable<IRenderable> ModifyRender(Actor self, WorldRenderer wr, IEnumerable<IRenderable> r)

View File

@@ -121,21 +121,24 @@ namespace OpenRA.Mods.RA.Move
if (SharesCell && world.ActorMap.HasFreeSubCell(cell))
return true;
var blockingActors = world.ActorMap.GetUnitsAt(cell)
.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)
if (checkTransientActors)
{
// Non-sharable unit can enter a cell with shareable units only if it can crush all of them
if (self == null || Crushes == null)
return false;
bool canIgnoreMovingAllies = self != null && !blockedByMovers;
bool needsCellExclusively = self == null || Crushes == null;
foreach(var a in world.ActorMap.GetUnitsAt(cell))
{
if (a == ignoreActor) continue;
if (blockingActors.Any(a => !(a.HasTrait<ICrushable>() &&
a.TraitsImplementing<ICrushable>().Any(b => b.CrushableBy(Crushes, self.Owner)))))
return false;
// Neutral/enemy units are blockers. Allied units that are moving are not blockers.
if (canIgnoreMovingAllies && self.Owner.Stances[a.Owner] == Stance.Ally && IsMovingInMyDirection(self, a)) continue;
// 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;