Skip to content

Commit

Permalink
Update frozen actors only when required.
Browse files Browse the repository at this point in the history
Previously, actors that were visible would refresh their frozen actor state every tick in preparation for the actor becoming hidden, and the frozen actor appearing as a placeholder instead.

By using ICreatesFrozenActors.OnVisibilityChanged when can avoid refreshing the state constantly, and instead just refresh it the moment the frozen actor needs to appear. This provides a nice performance improvement on the cost on managing frozen actors.
  • Loading branch information
RoosterDragon authored and abcdefg30 committed Aug 7, 2022
1 parent e827e99 commit bbf5970
Showing 1 changed file with 20 additions and 35 deletions.
55 changes: 20 additions & 35 deletions OpenRA.Mods.Common/Traits/Modifiers/FrozenUnderFog.cs
Expand Up @@ -26,7 +26,7 @@ public class FrozenUnderFogInfo : TraitInfo, Requires<BuildingInfo>, IDefaultVis
public override object Create(ActorInitializer init) { return new FrozenUnderFog(init, this); }
}

public class FrozenUnderFog : ICreatesFrozenActors, IRenderModifier, IDefaultVisibility, ITick, ITickRender, ISync, INotifyCreated, INotifyOwnerChanged, INotifyActorDisposing
public class FrozenUnderFog : ICreatesFrozenActors, IRenderModifier, IDefaultVisibility, ITickRender, ISync, INotifyCreated, INotifyOwnerChanged, INotifyActorDisposing
{
[Sync]
public int VisibilityHash;
Expand Down Expand Up @@ -72,6 +72,18 @@ void INotifyCreated.Created(Actor self)
player.PlayerActor.Trait<FrozenActorLayer>().Add(frozenActor);
return new FrozenState(frozenActor) { IsVisible = startsRevealed };
});

// Set the initial visibility state
// This relies on actor.GetTargetablePositions(), which is also setup up in Created.
// Since we can't be sure whether our method will run after theirs, defer by a frame.
if (startsRevealed)
self.World.AddFrameEndTask(_ =>
{
for (var playerIndex = 0; playerIndex < frozenStates.Count; playerIndex++)
UpdateFrozenActor(frozenStates[playerIndex].FrozenActor, playerIndex);
});

created = true;
}

void UpdateFrozenActor(FrozenActor frozenActor, int playerIndex)
Expand All @@ -86,9 +98,13 @@ void ICreatesFrozenActors.OnVisibilityChanged(FrozenActor frozen)
if (!created)
return;

// Update state visibility to match the frozen actor to ensure consistency within the tick
// The rest of the state will be updated by ITick.Tick below
frozenStates[frozen.Viewer].IsVisible = !frozen.Visible;
// Update state visibility to match the frozen actor to ensure consistency
var state = frozenStates[frozen.Viewer];
var isVisible = !frozen.Visible;
state.IsVisible = isVisible;

if (isVisible)
UpdateFrozenActor(frozen, frozen.Viewer.World.Players.IndexOf(frozen.Viewer));
}

bool IsVisibleInner(Player byPlayer)
Expand All @@ -109,37 +125,6 @@ public bool IsVisible(Actor self, Player byPlayer)
return info.AlwaysVisibleRelationships.HasRelationship(relationship) || IsVisibleInner(byPlayer);
}

void ITick.Tick(Actor self)
{
if (self.Disposed)
return;

// Set the initial visibility state
// This relies on actor.GetTargetablePositions(), which is not safe to use from Created
// so we defer until the first real tick.
if (!created && startsRevealed)
{
for (var playerIndex = 0; playerIndex < frozenStates.Count; playerIndex++)
UpdateFrozenActor(frozenStates[playerIndex].FrozenActor, playerIndex);

created = true;
return;
}

VisibilityHash = 0;

for (var playerIndex = 0; playerIndex < frozenStates.Count; playerIndex++)
{
var state = frozenStates[playerIndex];
var frozenActor = state.FrozenActor;
var isVisible = !frozenActor.Visible;
state.IsVisible = isVisible;

if (isVisible)
UpdateFrozenActor(frozenActor, playerIndex);
}
}

void ITickRender.TickRender(WorldRenderer wr, Actor self)
{
IRenderable[] renderables = null;
Expand Down

0 comments on commit bbf5970

Please sign in to comment.