From 6234e311ca86c86f4773d98e909ff6e2f5dc9b1d Mon Sep 17 00:00:00 2001 From: RoosterDragon Date: Mon, 2 Mar 2015 21:25:31 +0000 Subject: [PATCH] Speed up shroud visibility updates. A unit with the CreatesShroud or RevealsShroud trait must update all shrouds when it changes position. Calculating the tiles it can currently see is an expensive calculation that previously had to be repeated for every shroud that needed to know these tiles. Now, we lazily populate a ref parameter to allow it to be reused for an actor if possible. The biggest improvement comes from the fact that allied players can re-use this calculation when updating their shrouds. Since many games includes a neutral player allied to both sides, most games will see a decent speedup. --- OpenRA.Game/Traits/CreatesShroud.cs | 5 +-- OpenRA.Game/Traits/RevealsShroud.cs | 4 +-- OpenRA.Game/Traits/World/Shroud.cs | 52 ++++++++++++++++++++--------- 3 files changed, 38 insertions(+), 23 deletions(-) diff --git a/OpenRA.Game/Traits/CreatesShroud.cs b/OpenRA.Game/Traits/CreatesShroud.cs index f0abadd80a..a1d7a80fbf 100644 --- a/OpenRA.Game/Traits/CreatesShroud.cs +++ b/OpenRA.Game/Traits/CreatesShroud.cs @@ -36,10 +36,7 @@ namespace OpenRA.Traits { cachedLocation = self.Location; cachedDisabled = disabled; - - var shroud = self.World.Players.Select(p => p.Shroud); - foreach (var s in shroud) - s.UpdateShroudGeneration(self); + Shroud.UpdateShroudGeneration(self.World.Players.Select(p => p.Shroud), self); } } diff --git a/OpenRA.Game/Traits/RevealsShroud.cs b/OpenRA.Game/Traits/RevealsShroud.cs index 5b7a2c8219..c1c999ce13 100644 --- a/OpenRA.Game/Traits/RevealsShroud.cs +++ b/OpenRA.Game/Traits/RevealsShroud.cs @@ -33,9 +33,7 @@ namespace OpenRA.Traits if (cachedLocation != self.Location) { cachedLocation = self.Location; - - foreach (var s in self.World.Players.Select(p => p.Shroud)) - s.UpdateVisibility(self); + Shroud.UpdateVisibility(self.World.Players.Select(p => p.Shroud), self); } } diff --git a/OpenRA.Game/Traits/World/Shroud.cs b/OpenRA.Game/Traits/World/Shroud.cs index 1d04d8fea5..5898b26e23 100644 --- a/OpenRA.Game/Traits/World/Shroud.cs +++ b/OpenRA.Game/Traits/World/Shroud.cs @@ -73,10 +73,10 @@ namespace OpenRA.Traits generatedShroudCount = new CellLayer(map); explored = new CellLayer(map); - self.World.ActorAdded += AddVisibility; + self.World.ActorAdded += a => { CPos[] visible = null; AddVisibility(a, ref visible); }; self.World.ActorRemoved += RemoveVisibility; - self.World.ActorAdded += AddShroudGeneration; + self.World.ActorAdded += a => { CPos[] shrouded = null; AddShroudGeneration(a, ref shrouded); }; self.World.ActorRemoved += RemoveShroudGeneration; fogVisibilities = Exts.Lazy(() => self.TraitsImplementing().ToArray()); @@ -98,6 +98,25 @@ namespace OpenRA.Traits Hash += 1; } + public static void UpdateVisibility(IEnumerable shrouds, Actor actor) + { + CPos[] visbility = null; + foreach (var shroud in shrouds) + shroud.UpdateVisibility(actor, ref visbility); + } + + public static void UpdateShroudGeneration(IEnumerable shrouds, Actor actor) + { + CPos[] shrouded = null; + foreach (var shroud in shrouds) + shroud.UpdateShroudGeneration(actor, ref shrouded); + } + + static CPos[] FindVisibleTiles(Actor actor, WRange range) + { + return GetVisOrigins(actor).SelectMany(o => FindVisibleTiles(actor.World, o, range)).Distinct().ToArray(); + } + static IEnumerable FindVisibleTiles(World world, CPos position, WRange radius) { var map = world.Map; @@ -110,17 +129,15 @@ namespace OpenRA.Traits yield return cell; } - void AddVisibility(Actor a) + void AddVisibility(Actor a, ref CPos[] visible) { var rs = a.TraitOrDefault(); if (rs == null || !a.Owner.IsAlliedWith(self.Owner) || rs.Range == WRange.Zero) return; - var origins = GetVisOrigins(a); - var visible = origins.SelectMany(o => FindVisibleTiles(a.World, o, rs.Range)) - .Distinct().ToArray(); + // Lazily generate the visible tiles, allowing the caller to re-use them if desired. + visible = visible ?? FindVisibleTiles(a, rs.Range); - // Update visibility foreach (var c in visible) { visibleCount[c]++; @@ -147,24 +164,25 @@ namespace OpenRA.Traits Invalidate(); } - public void UpdateVisibility(Actor a) + void UpdateVisibility(Actor a, ref CPos[] visible) { // Actors outside the world don't have any vis if (!a.IsInWorld) return; RemoveVisibility(a); - AddVisibility(a); + AddVisibility(a, ref visible); } - void AddShroudGeneration(Actor a) + void AddShroudGeneration(Actor a, ref CPos[] shrouded) { var cs = a.TraitOrDefault(); if (cs == null || a.Owner.IsAlliedWith(self.Owner) || cs.Range == WRange.Zero) return; - var shrouded = GetVisOrigins(a).SelectMany(o => FindVisibleTiles(a.World, o, cs.Range)) - .Distinct().ToArray(); + // Lazily generate the shrouded tiles, allowing the caller to re-use them if desired. + shrouded = shrouded ?? FindVisibleTiles(a, cs.Range); + foreach (var c in shrouded) generatedShroudCount[c]++; @@ -188,10 +206,10 @@ namespace OpenRA.Traits Invalidate(); } - public void UpdateShroudGeneration(Actor a) + void UpdateShroudGeneration(Actor a, ref CPos[] shrouded) { RemoveShroudGeneration(a); - AddShroudGeneration(a); + AddShroudGeneration(a, ref shrouded); } public void UpdatePlayerStance(World w, Player player, Stance oldStance, Stance newStance) @@ -201,8 +219,10 @@ namespace OpenRA.Traits foreach (var a in w.Actors.Where(a => a.Owner == player)) { - UpdateVisibility(a); - UpdateShroudGeneration(a); + CPos[] visible = null; + UpdateVisibility(a, ref visible); + CPos[] shrouded = null; + UpdateShroudGeneration(a, ref shrouded); } }