Improve performance of FrozenActorLayer.Tick

By adding a UpdateVisibilityNextTick flag against every FrozenActor to track when a visibility update is required, we can remove the dirtyFrozenActorIds set in FrozenActorLayer. In the Tick method we can now avoid a set lookup.

Also, don't create the frozenActorsToRemove list until we need one to avoid an allocation.
This commit is contained in:
RoosterDragon
2024-10-05 11:34:42 +01:00
committed by abcdefg30
parent 6794b2dc40
commit d010157611

View File

@@ -69,6 +69,7 @@ namespace OpenRA.Traits
public bool Shrouded { get; private set; } public bool Shrouded { get; private set; }
public bool NeedRenderables { get; set; } public bool NeedRenderables { get; set; }
public bool UpdateVisibilityNextTick { get; set; }
public IRenderable[] Renderables = NoRenderables; public IRenderable[] Renderables = NoRenderables;
public Rectangle[] ScreenBounds = NoBounds; public Rectangle[] ScreenBounds = NoBounds;
@@ -154,10 +155,15 @@ namespace OpenRA.Traits
{ {
if (flashTicks > 0) if (flashTicks > 0)
flashTicks--; flashTicks--;
if (UpdateVisibilityNextTick)
UpdateVisibility();
} }
public void UpdateVisibility() void UpdateVisibility()
{ {
UpdateVisibilityNextTick = false;
var wasVisible = Visible; var wasVisible = Visible;
Shrouded = true; Shrouded = true;
Visible = true; Visible = true;
@@ -248,8 +254,7 @@ namespace OpenRA.Traits
readonly World world; readonly World world;
readonly Player owner; readonly Player owner;
readonly Dictionary<uint, FrozenActor> frozenActorsById; readonly Dictionary<uint, FrozenActor> frozenActorsById;
readonly SpatiallyPartitioned<uint> partitionedFrozenActorIds; readonly SpatiallyPartitioned<FrozenActor> partitionedFrozenActors;
readonly HashSet<uint> dirtyFrozenActorIds = new();
public FrozenActorLayer(Actor self, FrozenActorLayerInfo info) public FrozenActorLayer(Actor self, FrozenActorLayerInfo info)
{ {
@@ -258,22 +263,26 @@ namespace OpenRA.Traits
owner = self.Owner; owner = self.Owner;
frozenActorsById = new Dictionary<uint, FrozenActor>(); frozenActorsById = new Dictionary<uint, FrozenActor>();
partitionedFrozenActorIds = new SpatiallyPartitioned<uint>( partitionedFrozenActors = new SpatiallyPartitioned<FrozenActor>(
world.Map.MapSize.X, world.Map.MapSize.Y, binSize); world.Map.MapSize.X, world.Map.MapSize.Y, binSize);
self.Trait<Shroud>().OnShroudChanged += uv => dirtyFrozenActorIds.UnionWith(partitionedFrozenActorIds.At(new int2(uv.U, uv.V))); self.Trait<Shroud>().OnShroudChanged += uv =>
{
foreach (var fa in partitionedFrozenActors.At(new int2(uv.U, uv.V)))
fa.UpdateVisibilityNextTick = true;
};
} }
public void Add(FrozenActor fa) public void Add(FrozenActor fa)
{ {
frozenActorsById.Add(fa.ID, fa); frozenActorsById.Add(fa.ID, fa);
world.ScreenMap.AddOrUpdate(owner, fa); world.ScreenMap.AddOrUpdate(owner, fa);
partitionedFrozenActorIds.Add(fa.ID, FootprintBounds(fa)); partitionedFrozenActors.Add(fa, FootprintBounds(fa));
} }
public void Remove(FrozenActor fa) public void Remove(FrozenActor fa)
{ {
partitionedFrozenActorIds.Remove(fa.ID); partitionedFrozenActors.Remove(fa);
world.ScreenMap.Remove(owner, fa); world.ScreenMap.Remove(owner, fa);
frozenActorsById.Remove(fa.ID); frozenActorsById.Remove(fa.ID);
} }
@@ -303,7 +312,7 @@ namespace OpenRA.Traits
void ITick.Tick(Actor self) void ITick.Tick(Actor self)
{ {
var frozenActorsToRemove = new List<FrozenActor>(); List<FrozenActor> frozenActorsToRemove = null;
VisibilityHash = 0; VisibilityHash = 0;
FrozenHash = 0; FrozenHash = 0;
@@ -315,19 +324,19 @@ namespace OpenRA.Traits
var frozenActor = kvp.Value; var frozenActor = kvp.Value;
frozenActor.Tick(); frozenActor.Tick();
if (dirtyFrozenActorIds.Contains(id))
frozenActor.UpdateVisibility();
if (frozenActor.Visible) if (frozenActor.Visible)
VisibilityHash += hash; VisibilityHash += hash;
else if (frozenActor.Actor == null) else if (frozenActor.Actor == null)
{
frozenActorsToRemove ??= new List<FrozenActor>();
frozenActorsToRemove.Add(frozenActor); frozenActorsToRemove.Add(frozenActor);
}
} }
dirtyFrozenActorIds.Clear(); if (frozenActorsToRemove != null)
foreach (var fa in frozenActorsToRemove)
foreach (var fa in frozenActorsToRemove) Remove(fa);
Remove(fa);
} }
public virtual IEnumerable<IRenderable> Render(Actor self, WorldRenderer wr) public virtual IEnumerable<IRenderable> Render(Actor self, WorldRenderer wr)
@@ -355,8 +364,7 @@ namespace OpenRA.Traits
{ {
var tl = region.TopLeft; var tl = region.TopLeft;
var br = region.BottomRight; var br = region.BottomRight;
return partitionedFrozenActorIds.InBox(Rectangle.FromLTRB(tl.X, tl.Y, br.X, br.Y)) return partitionedFrozenActors.InBox(Rectangle.FromLTRB(tl.X, tl.Y, br.X, br.Y))
.Select(FromID)
.Where(fa => fa.IsValid && (!onlyVisible || fa.Visible)); .Where(fa => fa.IsValid && (!onlyVisible || fa.Visible));
} }
@@ -368,8 +376,7 @@ namespace OpenRA.Traits
var br = centerCell + new CVec(cellRange, cellRange); var br = centerCell + new CVec(cellRange, cellRange);
// Target ranges are calculated in 2D, so ignore height differences // Target ranges are calculated in 2D, so ignore height differences
return partitionedFrozenActorIds.InBox(Rectangle.FromLTRB(tl.X, tl.Y, br.X, br.Y)) return partitionedFrozenActors.InBox(Rectangle.FromLTRB(tl.X, tl.Y, br.X, br.Y))
.Select(FromID)
.Where(fa => fa.IsValid && .Where(fa => fa.IsValid &&
(!onlyVisible || fa.Visible) && (!onlyVisible || fa.Visible) &&
(fa.CenterPosition - origin).HorizontalLengthSquared <= r.LengthSquared); (fa.CenterPosition - origin).HorizontalLengthSquared <= r.LengthSquared);