diff --git a/OpenRA.Game/Graphics/WorldRenderer.cs b/OpenRA.Game/Graphics/WorldRenderer.cs index e7e4766251..5769666ce5 100644 --- a/OpenRA.Game/Graphics/WorldRenderer.cs +++ b/OpenRA.Game/Graphics/WorldRenderer.cs @@ -43,6 +43,8 @@ namespace OpenRA.Graphics readonly List preparedOverlayRenderables = new List(); readonly List preparedAnnotationRenderables = new List(); + readonly List renderablesBuffer = new List(); + bool lastDepthPreviewEnabled; internal WorldRenderer(ModData modData, World world) @@ -107,78 +109,117 @@ namespace OpenRA.Graphics palettes[name].Palette = pal; } - IEnumerable GenerateRenderables() + // PERF: Avoid LINQ. + void GenerateRenderables() { - var actors = onScreenActors.Append(World.WorldActor); - if (World.RenderPlayer != null) - actors = actors.Append(World.RenderPlayer.PlayerActor); + foreach (var actor in onScreenActors) + renderablesBuffer.AddRange(actor.Render(this)); + + renderablesBuffer.AddRange(World.WorldActor.Render(this)); + + if (World.RenderPlayer != null) + renderablesBuffer.AddRange(World.RenderPlayer.PlayerActor.Render(this)); - var worldRenderables = actors.SelectMany(a => a.Render(this)); if (World.OrderGenerator != null) - worldRenderables = worldRenderables.Concat(World.OrderGenerator.Render(this, World)); + renderablesBuffer.AddRange(World.OrderGenerator.Render(this, World)); // Unpartitioned effects - worldRenderables = worldRenderables.Concat(World.UnpartitionedEffects.SelectMany(e => e.Render(this))); + foreach (var e in World.UnpartitionedEffects) + renderablesBuffer.AddRange(e.Render(this)); // Partitioned, currently on-screen effects - var effectRenderables = World.ScreenMap.RenderableEffectsInBox(Viewport.TopLeft, Viewport.BottomRight); - worldRenderables = worldRenderables.Concat(effectRenderables.SelectMany(e => e.Render(this))); + foreach (var e in World.ScreenMap.RenderableEffectsInBox(Viewport.TopLeft, Viewport.BottomRight)) + renderablesBuffer.AddRange(e.Render(this)); - worldRenderables = worldRenderables.OrderBy(RenderableScreenZPositionComparisonKey); + foreach (var renderable in renderablesBuffer.OrderBy(RenderableScreenZPositionComparisonKey)) + preparedRenderables.Add(renderable.PrepareRender(this)); - return worldRenderables.Select(r => r.PrepareRender(this)); + // PERF: Reuse collection to avoid allocations. + renderablesBuffer.Clear(); } - IEnumerable GenerateOverlayRenderables() + // PERF: Avoid LINQ. + void GenerateOverlayRenderables() { - var actors = World.ActorsWithTrait() - .Where(a => a.Actor.IsInWorld && !a.Actor.Disposed && (!a.Trait.SpatiallyPartitionable || onScreenActors.Contains(a.Actor))) - .SelectMany(a => a.Trait.RenderAboveShroud(a.Actor, this)); + foreach (var a in World.ActorsWithTrait()) + { + if (!a.Actor.IsInWorld || a.Actor.Disposed || (a.Trait.SpatiallyPartitionable && !onScreenActors.Contains(a.Actor))) + continue; - var selected = World.Selection.Actors.Where(a => a.IsInWorld && !a.Disposed) - .SelectMany(a => a.TraitsImplementing() - .Where(t => !t.SpatiallyPartitionable || onScreenActors.Contains(a)) - .SelectMany(t => t.RenderAboveShroud(a, this))); + foreach (var renderable in a.Trait.RenderAboveShroud(a.Actor, this)) + preparedOverlayRenderables.Add(renderable.PrepareRender(this)); + } - var effects = World.Effects.Select(e => e as IEffectAboveShroud) - .Where(e => e != null) - .SelectMany(e => e.RenderAboveShroud(this)); + foreach (var a in World.Selection.Actors) + { + if (!a.IsInWorld || a.Disposed) + continue; + + foreach (var t in a.TraitsImplementing()) + { + if (t.SpatiallyPartitionable && !onScreenActors.Contains(a)) + continue; + + foreach (var renderable in t.RenderAboveShroud(a, this)) + preparedOverlayRenderables.Add(renderable.PrepareRender(this)); + } + } + + foreach (var e in World.Effects) + { + var ea = e as IEffectAboveShroud; + if (ea == null) + continue; + + foreach (var renderable in ea.RenderAboveShroud(this)) + preparedOverlayRenderables.Add(renderable.PrepareRender(this)); + } - var orderGenerator = SpriteRenderable.None; if (World.OrderGenerator != null) - orderGenerator = World.OrderGenerator.RenderAboveShroud(this, World); - - return actors - .Concat(selected) - .Concat(effects) - .Concat(orderGenerator) - .Select(r => r.PrepareRender(this)); + foreach (var renderable in World.OrderGenerator.RenderAboveShroud(this, World)) + preparedOverlayRenderables.Add(renderable.PrepareRender(this)); } - IEnumerable GenerateAnnotationRenderables() + // PERF: Avoid LINQ. + void GenerateAnnotationRenderables() { - var actors = World.ActorsWithTrait() - .Where(a => a.Actor.IsInWorld && !a.Actor.Disposed && (!a.Trait.SpatiallyPartitionable || onScreenActors.Contains(a.Actor))) - .SelectMany(a => a.Trait.RenderAnnotations(a.Actor, this)); + foreach (var a in World.ActorsWithTrait()) + { + if (!a.Actor.IsInWorld || a.Actor.Disposed || (a.Trait.SpatiallyPartitionable && !onScreenActors.Contains(a.Actor))) + continue; - var selected = World.Selection.Actors.Where(a => a.IsInWorld && !a.Disposed) - .SelectMany(a => a.TraitsImplementing() - .Where(t => !t.SpatiallyPartitionable || onScreenActors.Contains(a)) - .SelectMany(t => t.RenderAnnotations(a, this))); + foreach (var renderAnnotation in a.Trait.RenderAnnotations(a.Actor, this)) + preparedAnnotationRenderables.Add(renderAnnotation.PrepareRender(this)); + } - var effects = World.Effects.Select(e => e as IEffectAnnotation) - .Where(e => e != null) - .SelectMany(e => e.RenderAnnotation(this)); + foreach (var a in World.Selection.Actors) + { + if (!a.IsInWorld || a.Disposed) + continue; + + foreach (var t in a.TraitsImplementing()) + { + if (t.SpatiallyPartitionable && !onScreenActors.Contains(a)) + continue; + + foreach (var renderAnnotation in t.RenderAnnotations(a, this)) + preparedAnnotationRenderables.Add(renderAnnotation.PrepareRender(this)); + } + } + + foreach (var e in World.Effects) + { + var ea = e as IEffectAnnotation; + if (ea == null) + continue; + + foreach (var renderAnnotation in ea.RenderAnnotation(this)) + preparedAnnotationRenderables.Add(renderAnnotation.PrepareRender(this)); + } - var orderGenerator = SpriteRenderable.None; if (World.OrderGenerator != null) - orderGenerator = World.OrderGenerator.RenderAnnotations(this, World); - - return actors - .Concat(selected) - .Concat(effects) - .Concat(orderGenerator) - .Select(r => r.PrepareRender(this)); + foreach (var renderAnnotation in World.OrderGenerator.RenderAnnotations(this, World)) + preparedAnnotationRenderables.Add(renderAnnotation.PrepareRender(this)); } public void PrepareRenderables() @@ -190,9 +231,11 @@ namespace OpenRA.Graphics // PERF: Reuse collection to avoid allocations. onScreenActors.UnionWith(World.ScreenMap.RenderableActorsInBox(Viewport.TopLeft, Viewport.BottomRight)); - preparedRenderables.AddRange(GenerateRenderables()); - preparedOverlayRenderables.AddRange(GenerateOverlayRenderables()); - preparedAnnotationRenderables.AddRange(GenerateAnnotationRenderables()); + + GenerateRenderables(); + GenerateOverlayRenderables(); + GenerateAnnotationRenderables(); + onScreenActors.Clear(); } @@ -279,9 +322,12 @@ namespace OpenRA.Graphics foreach (var b in World.ScreenMap.MouseBounds(World.RenderPlayer)) { - var points = b.Vertices - .Select(p => Viewport.WorldToViewPx(p).ToFloat2()) - .ToArray(); + var points = new float2[b.Vertices.Length]; + for (var index = 0; index < b.Vertices.Length; index++) + { + var vertex = b.Vertices[index]; + points[index] = Viewport.WorldToViewPx(vertex).ToFloat2(); + } Game.Renderer.RgbaColorRenderer.DrawPolygon(points, 1, Color.OrangeRed); }