Refactor WorldRenderer to use less allocations

This commit is contained in:
teinarss
2020-07-01 22:17:54 +02:00
committed by abcdefg30
parent ae882b85a9
commit 67ff292d62

View File

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