From 87d35a75da951f4b40880c4ecc721551012f712d Mon Sep 17 00:00:00 2001 From: RoosterDragon Date: Thu, 26 Nov 2015 22:09:35 +0000 Subject: [PATCH 1/4] Merge dictionaries in GpsDot. This speeds up GpsDot.Tick as it requires only one dictionary lookup per player rather than two. --- OpenRA.Mods.RA/Effects/GpsDot.cs | 33 +++++++++++++++++++------------- 1 file changed, 20 insertions(+), 13 deletions(-) diff --git a/OpenRA.Mods.RA/Effects/GpsDot.cs b/OpenRA.Mods.RA/Effects/GpsDot.cs index d8515b5380..4f9f9c3082 100644 --- a/OpenRA.Mods.RA/Effects/GpsDot.cs +++ b/OpenRA.Mods.RA/Effects/GpsDot.cs @@ -37,14 +37,22 @@ namespace OpenRA.Mods.RA.Effects readonly GpsDotInfo info; readonly Animation anim; - readonly Dictionary showToPlayer = new Dictionary(); + readonly Dictionary stateByPlayer = new Dictionary(); + readonly Lazy huf; + readonly Lazy fuf; + readonly Lazy disguise; + readonly Lazy cloak; + readonly Cache frozen; - Lazy huf; - Lazy fuf; - Lazy disguise; - Lazy cloak; - Cache watcher; - Cache frozen; + class DotState + { + public readonly GpsWatcher Gps; + public bool IsVisible; + public DotState(GpsWatcher gps) + { + Gps = gps; + } + } public GpsDot(Actor self, GpsDotInfo info) { @@ -60,15 +68,14 @@ namespace OpenRA.Mods.RA.Effects disguise = Exts.Lazy(() => self.TraitOrDefault()); cloak = Exts.Lazy(() => self.TraitOrDefault()); - watcher = new Cache(p => p.PlayerActor.Trait()); frozen = new Cache(p => p.PlayerActor.Trait()); - showToPlayer = self.World.Players.ToDictionary(x => x, x => false); + stateByPlayer = self.World.Players.ToDictionary(p => p, p => new DotState(p.PlayerActor.Trait())); } public bool IsDotVisible(Player toPlayer) { - return showToPlayer[toPlayer]; + return stateByPlayer[toPlayer].IsVisible; } bool ShouldShowIndicator(Player toPlayer) @@ -106,14 +113,14 @@ namespace OpenRA.Mods.RA.Effects foreach (var player in self.World.Players) { - var gps = watcher[player]; - showToPlayer[player] = (gps.Granted || gps.GrantedAllies) && ShouldShowIndicator(player); + var state = stateByPlayer[player]; + state.IsVisible = (state.Gps.Granted || state.Gps.GrantedAllies) && ShouldShowIndicator(player); } } public IEnumerable Render(WorldRenderer wr) { - if (self.World.RenderPlayer == null || !showToPlayer[self.World.RenderPlayer] || self.Disposed) + if (self.World.RenderPlayer == null || !IsDotVisible(self.World.RenderPlayer) || self.Disposed) return SpriteRenderable.None; var palette = wr.Palette(info.IndicatorPalettePrefix + self.Owner.InternalName); From cbc090dd3873480a8f3b1d0cee982a9a3c97fdbc Mon Sep 17 00:00:00 2001 From: RoosterDragon Date: Sun, 6 Dec 2015 01:14:39 +0000 Subject: [PATCH 2/4] Ensure frozen actors are rendered on the first tick they become visible. The previous lazy rendering means the snapshot of the render state might be more up to date then when the frozen actor actually did become visible. Now, we take this snapshot as soon as needed. We still retain the performance of only doing this rendering when needed by avoiding extra rendering until the visibility cycles again. --- OpenRA.Game/Traits/Player/FrozenActorLayer.cs | 32 ++++++------------- .../Traits/Modifiers/FrozenUnderFog.cs | 29 +++++++++++++++-- 2 files changed, 36 insertions(+), 25 deletions(-) diff --git a/OpenRA.Game/Traits/Player/FrozenActorLayer.cs b/OpenRA.Game/Traits/Player/FrozenActorLayer.cs index 1e8a611385..55f6ad28c9 100644 --- a/OpenRA.Game/Traits/Player/FrozenActorLayer.cs +++ b/OpenRA.Game/Traits/Player/FrozenActorLayer.cs @@ -42,8 +42,11 @@ namespace OpenRA.Traits public bool Visible = true; public bool Shrouded { get; private set; } - public bool NeedRenderables { get; private set; } - public bool IsRendering { get; private set; } + public bool NeedRenderables { get; set; } + public IRenderable[] Renderables = NoRenderables; + static readonly IRenderable[] NoRenderables = new IRenderable[0]; + + int flashTicks; public FrozenActor(Actor self, PPos[] footprint, Shroud shroud, bool startsRevealed) { @@ -69,11 +72,6 @@ namespace OpenRA.Traits public ActorInfo Info { get { return actor.Info; } } public Actor Actor { get { return !actor.IsDead ? actor : null; } } - static readonly IRenderable[] NoRenderables = new IRenderable[0]; - - int flashTicks; - IRenderable[] renderables = NoRenderables; - public void Tick() { UpdateVisibility(); @@ -103,8 +101,7 @@ namespace OpenRA.Traits Shrouded = false; } - if (Visible && !wasVisible) - NeedRenderables = true; + NeedRenderables = Visible && !wasVisible; } public void Flash() @@ -114,31 +111,20 @@ namespace OpenRA.Traits public IEnumerable Render(WorldRenderer wr) { - if (NeedRenderables) - { - NeedRenderables = false; - if (!actor.Disposed) - { - IsRendering = true; - renderables = actor.Render(wr).ToArray(); - IsRendering = false; - } - } - if (Shrouded) return NoRenderables; if (flashTicks > 0 && flashTicks % 2 == 0) { var highlight = wr.Palette("highlight"); - return renderables.Concat(renderables.Where(r => !r.IsDecoration) + return Renderables.Concat(Renderables.Where(r => !r.IsDecoration) .Select(r => r.WithPalette(highlight))); } - return renderables; + return Renderables; } - public bool HasRenderables { get { return !Shrouded && renderables.Any(); } } + public bool HasRenderables { get { return !Shrouded && Renderables.Any(); } } public bool ShouldBeRemoved(Player owner) { diff --git a/OpenRA.Mods.Common/Traits/Modifiers/FrozenUnderFog.cs b/OpenRA.Mods.Common/Traits/Modifiers/FrozenUnderFog.cs index d1bb72ee2d..0017a7e605 100644 --- a/OpenRA.Mods.Common/Traits/Modifiers/FrozenUnderFog.cs +++ b/OpenRA.Mods.Common/Traits/Modifiers/FrozenUnderFog.cs @@ -27,7 +27,7 @@ namespace OpenRA.Mods.Common.Traits public object Create(ActorInitializer init) { return new FrozenUnderFog(init, this); } } - public class FrozenUnderFog : IRenderModifier, IDefaultVisibility, ITick, ISync + public class FrozenUnderFog : IRenderModifier, IDefaultVisibility, ITick, ITickRender, ISync { [Sync] public int VisibilityHash; @@ -41,6 +41,7 @@ namespace OpenRA.Mods.Common.Traits ITooltip tooltip; Health health; bool initialized; + bool isRendering; class FrozenState { @@ -143,11 +144,35 @@ namespace OpenRA.Mods.Common.Traits initialized = true; } + public void TickRender(WorldRenderer wr, Actor self) + { + if (!initialized) + return; + + IRenderable[] renderables = null; + foreach (var player in self.World.Players) + { + var frozen = stateByPlayer[player].FrozenActor; + if (!frozen.NeedRenderables) + continue; + + if (renderables == null) + { + isRendering = true; + renderables = self.Render(wr).ToArray(); + isRendering = false; + } + + frozen.NeedRenderables = false; + frozen.Renderables = renderables; + } + } + public IEnumerable ModifyRender(Actor self, WorldRenderer wr, IEnumerable r) { return IsVisible(self, self.World.RenderPlayer) || - (initialized && stateByPlayer[self.World.RenderPlayer].FrozenActor.IsRendering) ? + (initialized && isRendering) ? r : SpriteRenderable.None; } } From f96d02a79482f10a303e92a83d0a099e5c05ea22 Mon Sep 17 00:00:00 2001 From: RoosterDragon Date: Sun, 6 Dec 2015 01:17:42 +0000 Subject: [PATCH 3/4] Fix GPS dot relying on frozen actor render state. The GPS dot was not displayed if a frozen actor for it was already visible. However, this was mistakenly affecting visibility checks in the world. Now, we only avoid rendering it when the frozen actor is present, but the indicator is still regarded as visible for visilbity checks in the world. --- OpenRA.Mods.RA/Effects/GpsDot.cs | 34 ++++++++++++++++++++++---------- 1 file changed, 24 insertions(+), 10 deletions(-) diff --git a/OpenRA.Mods.RA/Effects/GpsDot.cs b/OpenRA.Mods.RA/Effects/GpsDot.cs index 4f9f9c3082..1ff8497ef9 100644 --- a/OpenRA.Mods.RA/Effects/GpsDot.cs +++ b/OpenRA.Mods.RA/Effects/GpsDot.cs @@ -47,7 +47,8 @@ namespace OpenRA.Mods.RA.Effects class DotState { public readonly GpsWatcher Gps; - public bool IsVisible; + public bool IsTargetable; + public bool ShouldRender; public DotState(GpsWatcher gps) { Gps = gps; @@ -75,11 +76,13 @@ namespace OpenRA.Mods.RA.Effects public bool IsDotVisible(Player toPlayer) { - return stateByPlayer[toPlayer].IsVisible; + return stateByPlayer[toPlayer].IsTargetable; } - bool ShouldShowIndicator(Player toPlayer) + bool IsTargetableBy(Player toPlayer, out bool shouldRenderIndicator) { + shouldRenderIndicator = false; + if (cloak.Value != null && cloak.Value.Cloaked) return false; @@ -88,19 +91,27 @@ namespace OpenRA.Mods.RA.Effects if (huf.Value != null && !huf.Value.IsVisible(self, toPlayer) && toPlayer.Shroud.IsExplored(self.CenterPosition)) + { + var f1 = FrozenActorForPlayer(toPlayer); + shouldRenderIndicator = f1 == null || !f1.HasRenderables; return true; + } if (fuf.Value == null) return false; - var f = frozen[toPlayer].FromID(self.ActorID); - if (f == null) + var f2 = FrozenActorForPlayer(toPlayer); + if (f2 == null) return false; - if (f.HasRenderables || f.NeedRenderables) - return false; + shouldRenderIndicator = !f2.HasRenderables; - return f.Visible && !f.Shrouded; + return f2.Visible && !f2.Shrouded; + } + + FrozenActor FrozenActorForPlayer(Player player) + { + return frozen[player].FromID(self.ActorID); } public void Tick(World world) @@ -114,13 +125,16 @@ namespace OpenRA.Mods.RA.Effects foreach (var player in self.World.Players) { var state = stateByPlayer[player]; - state.IsVisible = (state.Gps.Granted || state.Gps.GrantedAllies) && ShouldShowIndicator(player); + var shouldRender = false; + var targetable = (state.Gps.Granted || state.Gps.GrantedAllies) && IsTargetableBy(player, out shouldRender); + state.IsTargetable = targetable; + state.ShouldRender = targetable && shouldRender; } } public IEnumerable Render(WorldRenderer wr) { - if (self.World.RenderPlayer == null || !IsDotVisible(self.World.RenderPlayer) || self.Disposed) + if (self.World.RenderPlayer == null || !stateByPlayer[self.World.RenderPlayer].ShouldRender || self.Disposed) return SpriteRenderable.None; var palette = wr.Palette(info.IndicatorPalettePrefix + self.Owner.InternalName); From 62a932272cbb81306cd7e650058d4d48d63e5730 Mon Sep 17 00:00:00 2001 From: RoosterDragon Date: Sun, 6 Dec 2015 01:26:09 +0000 Subject: [PATCH 4/4] Fix GPS refreshing for incorrect player. GpsWatcher.RefreshGps was wrongly using the render player in simulation code. --- OpenRA.Mods.RA/Traits/SupportPowers/GpsPower.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/OpenRA.Mods.RA/Traits/SupportPowers/GpsPower.cs b/OpenRA.Mods.RA/Traits/SupportPowers/GpsPower.cs index 16ca4eb1ea..5d411145e0 100644 --- a/OpenRA.Mods.RA/Traits/SupportPowers/GpsPower.cs +++ b/OpenRA.Mods.RA/Traits/SupportPowers/GpsPower.cs @@ -63,7 +63,7 @@ namespace OpenRA.Mods.RA.Traits foreach (var i in atek.World.ActorsWithTrait()) i.Trait.RefreshGranted(); - if ((Granted || GrantedAllies) && atek.Owner.IsAlliedWith(atek.World.RenderPlayer)) + if ((Granted || GrantedAllies) && atek.Owner.IsAlliedWith(Owner)) atek.Owner.Shroud.ExploreAll(atek.World); }