Add FirstNonEmptyBounds method for IDecorationBounds interface.

This provides a more efficient way of determining the bounds by avoiding LINQ. A helper that works directly on arrays prevents allocation of an enumerator when the collection is know to be an array.
This commit is contained in:
RoosterDragon
2017-12-22 12:55:05 +00:00
committed by reaperrr
parent 3b642b1a79
commit d2f2029caf
10 changed files with 38 additions and 21 deletions

View File

@@ -110,6 +110,34 @@ namespace OpenRA.Traits
// HACK: This provides a shim for legacy code until it can be rewritten // HACK: This provides a shim for legacy code until it can be rewritten
public interface IDecorationBounds { Rectangle DecorationBounds(Actor self, WorldRenderer wr); } public interface IDecorationBounds { Rectangle DecorationBounds(Actor self, WorldRenderer wr); }
public interface IDecorationBoundsInfo : ITraitInfoInterface { } public interface IDecorationBoundsInfo : ITraitInfoInterface { }
public static class DecorationBoundsExtensions
{
public static Rectangle FirstNonEmptyBounds(this IEnumerable<IDecorationBounds> decorationBounds, Actor self, WorldRenderer wr)
{
// PERF: Avoid LINQ.
foreach (var decoration in decorationBounds)
{
var bounds = decoration.DecorationBounds(self, wr);
if (!bounds.IsEmpty)
return bounds;
}
return Rectangle.Empty;
}
public static Rectangle FirstNonEmptyBounds(this IDecorationBounds[] decorationBounds, Actor self, WorldRenderer wr)
{
// PERF: Avoid LINQ.
foreach (var decoration in decorationBounds)
{
var bounds = decoration.DecorationBounds(self, wr);
if (!bounds.IsEmpty)
return bounds;
}
return Rectangle.Empty;
}
}
public interface IIssueOrder public interface IIssueOrder
{ {

View File

@@ -163,10 +163,7 @@ namespace OpenRA.Mods.Cnc.Traits
{ {
if (unit.CanBeViewedByPlayer(manager.Self.Owner)) if (unit.CanBeViewedByPlayer(manager.Self.Owner))
{ {
var bounds = unit.TraitsImplementing<IDecorationBounds>() var bounds = unit.TraitsImplementing<IDecorationBounds>().FirstNonEmptyBounds(unit, wr);
.Select(b => b.DecorationBounds(unit, wr))
.FirstOrDefault(b => !b.IsEmpty);
yield return new SelectionBoxRenderable(unit, bounds, Color.Red); yield return new SelectionBoxRenderable(unit, bounds, Color.Red);
} }
} }
@@ -281,10 +278,7 @@ namespace OpenRA.Mods.Cnc.Traits
{ {
if (unit.CanBeViewedByPlayer(manager.Self.Owner)) if (unit.CanBeViewedByPlayer(manager.Self.Owner))
{ {
var bounds = unit.TraitsImplementing<IDecorationBounds>() var bounds = unit.TraitsImplementing<IDecorationBounds>().FirstNonEmptyBounds(unit, wr);
.Select(b => b.DecorationBounds(unit, wr))
.FirstOrDefault(b => !b.IsEmpty);
yield return new SelectionBoxRenderable(unit, bounds, Color.Red); yield return new SelectionBoxRenderable(unit, bounds, Color.Red);
} }
} }

View File

@@ -50,7 +50,7 @@ namespace OpenRA.Mods.Common.Traits.Render
public IEnumerable<IRenderable> Render(Actor self, WorldRenderer wr) public IEnumerable<IRenderable> Render(Actor self, WorldRenderer wr)
{ {
var bounds = decorationBounds.Select(b => b.DecorationBounds(self, wr)).FirstOrDefault(b => !b.IsEmpty); var bounds = decorationBounds.FirstNonEmptyBounds(self, wr);
var spaceBuffer = (int)(10 / wr.Viewport.Zoom); var spaceBuffer = (int)(10 / wr.Viewport.Zoom);
var effectPos = wr.ProjectedPosition(new int2((bounds.Left + bounds.Right) / 2, bounds.Y - spaceBuffer)); var effectPos = wr.ProjectedPosition(new int2((bounds.Left + bounds.Right) / 2, bounds.Y - spaceBuffer));

View File

@@ -89,7 +89,7 @@ namespace OpenRA.Mods.Common.Traits.Render
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;
var bounds = decorationBounds.Select(b => b.DecorationBounds(self, wr)).FirstOrDefault(b => !b.IsEmpty); var bounds = decorationBounds.FirstNonEmptyBounds(self, wr);
// Health bars are shown when: // Health bars are shown when:
// * actor is selected // * actor is selected

View File

@@ -106,7 +106,7 @@ namespace OpenRA.Mods.Common.Traits.Render
if (!ShouldRender(self) || self.World.FogObscures(self)) if (!ShouldRender(self) || self.World.FogObscures(self))
return Enumerable.Empty<IRenderable>(); return Enumerable.Empty<IRenderable>();
var bounds = decorationBounds.Select(b => b.DecorationBounds(self, wr)).FirstOrDefault(b => !b.IsEmpty); var bounds = decorationBounds.FirstNonEmptyBounds(self, wr);
var halfSize = (0.5f * Anim.Image.Size.XY).ToInt2(); var halfSize = (0.5f * Anim.Image.Size.XY).ToInt2();
var boundsOffset = new int2(bounds.Left + bounds.Right, bounds.Top + bounds.Bottom) / 2; var boundsOffset = new int2(bounds.Left + bounds.Right, bounds.Top + bounds.Bottom) / 2;

View File

@@ -72,7 +72,7 @@ namespace OpenRA.Mods.Common.Traits.Render
pipImages.PlayFetchIndex(Info.GroupSequence, () => (int)group); pipImages.PlayFetchIndex(Info.GroupSequence, () => (int)group);
var bounds = decorationBounds.Select(b => b.DecorationBounds(self, wr)).FirstOrDefault(b => !b.IsEmpty); var bounds = decorationBounds.FirstNonEmptyBounds(self, wr);
var boundsOffset = 0.5f * new float2(bounds.Left + bounds.Right, bounds.Top + bounds.Bottom); var boundsOffset = 0.5f * new float2(bounds.Left + bounds.Right, bounds.Top + bounds.Bottom);
if (Info.ReferencePoint.HasFlag(ReferencePoints.Top)) if (Info.ReferencePoint.HasFlag(ReferencePoints.Top))
boundsOffset -= new float2(0, 0.5f * bounds.Height); boundsOffset -= new float2(0, 0.5f * bounds.Height);

View File

@@ -87,7 +87,7 @@ namespace OpenRA.Mods.Common.Traits.Render
if (group == null) if (group == null)
yield break; yield break;
var bounds = decorationBounds.Select(b => b.DecorationBounds(self, wr)).FirstOrDefault(b => !b.IsEmpty); var bounds = decorationBounds.FirstNonEmptyBounds(self, wr);
var number = group.Value.ToString(); var number = group.Value.ToString();
var halfSize = font.Measure(number) / 2; var halfSize = font.Measure(number) / 2;

View File

@@ -105,7 +105,7 @@ namespace OpenRA.Mods.Common.Traits.Render
if (!ShouldRender(self) || self.World.FogObscures(self)) if (!ShouldRender(self) || self.World.FogObscures(self))
return Enumerable.Empty<IRenderable>(); return Enumerable.Empty<IRenderable>();
var bounds = decorationBounds.Select(b => b.DecorationBounds(self, wr)).FirstOrDefault(b => !b.IsEmpty); var bounds = decorationBounds.FirstNonEmptyBounds(self, wr);
var halfSize = font.Measure(Info.Text) / 2; var halfSize = font.Measure(Info.Text) / 2;
var boundsOffset = new int2(bounds.Left + bounds.Right, bounds.Top + bounds.Bottom) / 2; var boundsOffset = new int2(bounds.Left + bounds.Right, bounds.Top + bounds.Bottom) / 2;

View File

@@ -138,9 +138,7 @@ namespace OpenRA.Mods.Common.Traits
var xy = wr.Viewport.ViewToWorld(Viewport.LastMousePos); var xy = wr.Viewport.ViewToWorld(Viewport.LastMousePos);
foreach (var unit in power.UnitsInRange(xy)) foreach (var unit in power.UnitsInRange(xy))
{ {
var bounds = unit.TraitsImplementing<IDecorationBounds>() var bounds = unit.TraitsImplementing<IDecorationBounds>().FirstNonEmptyBounds(unit, wr);
.Select(b => b.DecorationBounds(unit, wr))
.FirstOrDefault(b => !b.IsEmpty);
yield return new SelectionBoxRenderable(unit, bounds, Color.Red); yield return new SelectionBoxRenderable(unit, bounds, Color.Red);
} }
} }

View File

@@ -51,10 +51,7 @@ namespace OpenRA.Mods.Common.Widgets
// TODO: Integrate this with SelectionDecorations to unhardcode the *Renderable // TODO: Integrate this with SelectionDecorations to unhardcode the *Renderable
if (unit.Info.HasTraitInfo<SelectableInfo>()) if (unit.Info.HasTraitInfo<SelectableInfo>())
{ {
var bounds = unit.TraitsImplementing<IDecorationBounds>() var bounds = unit.TraitsImplementing<IDecorationBounds>().FirstNonEmptyBounds(unit, worldRenderer);
.Select(b => b.DecorationBounds(unit, worldRenderer))
.FirstOrDefault(b => !b.IsEmpty);
new SelectionBarsRenderable(unit, bounds, true, true).Render(worldRenderer); new SelectionBarsRenderable(unit, bounds, true, true).Render(worldRenderer);
} }
} }