From bbf5970bc16a41ac5f7a7324c99dd0c3a917418d Mon Sep 17 00:00:00 2001 From: RoosterDragon Date: Fri, 29 Jul 2022 19:09:46 +0100 Subject: [PATCH] Update frozen actors only when required. 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. --- .../Traits/Modifiers/FrozenUnderFog.cs | 55 +++++++------------ 1 file changed, 20 insertions(+), 35 deletions(-) diff --git a/OpenRA.Mods.Common/Traits/Modifiers/FrozenUnderFog.cs b/OpenRA.Mods.Common/Traits/Modifiers/FrozenUnderFog.cs index c982f337e8..1a896e29e0 100644 --- a/OpenRA.Mods.Common/Traits/Modifiers/FrozenUnderFog.cs +++ b/OpenRA.Mods.Common/Traits/Modifiers/FrozenUnderFog.cs @@ -26,7 +26,7 @@ namespace OpenRA.Mods.Common.Traits 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; @@ -72,6 +72,18 @@ namespace OpenRA.Mods.Common.Traits player.PlayerActor.Trait().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) @@ -86,9 +98,13 @@ namespace OpenRA.Mods.Common.Traits 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) @@ -109,37 +125,6 @@ namespace OpenRA.Mods.Common.Traits 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;