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.
This commit is contained in:
RoosterDragon
2015-03-02 21:25:31 +00:00
parent d135e58ad9
commit 6234e311ca
3 changed files with 38 additions and 23 deletions

View File

@@ -36,10 +36,7 @@ namespace OpenRA.Traits
{ {
cachedLocation = self.Location; cachedLocation = self.Location;
cachedDisabled = disabled; cachedDisabled = disabled;
Shroud.UpdateShroudGeneration(self.World.Players.Select(p => p.Shroud), self);
var shroud = self.World.Players.Select(p => p.Shroud);
foreach (var s in shroud)
s.UpdateShroudGeneration(self);
} }
} }

View File

@@ -33,9 +33,7 @@ namespace OpenRA.Traits
if (cachedLocation != self.Location) if (cachedLocation != self.Location)
{ {
cachedLocation = self.Location; cachedLocation = self.Location;
Shroud.UpdateVisibility(self.World.Players.Select(p => p.Shroud), self);
foreach (var s in self.World.Players.Select(p => p.Shroud))
s.UpdateVisibility(self);
} }
} }

View File

@@ -73,10 +73,10 @@ namespace OpenRA.Traits
generatedShroudCount = new CellLayer<short>(map); generatedShroudCount = new CellLayer<short>(map);
explored = new CellLayer<bool>(map); explored = new CellLayer<bool>(map);
self.World.ActorAdded += AddVisibility; self.World.ActorAdded += a => { CPos[] visible = null; AddVisibility(a, ref visible); };
self.World.ActorRemoved += RemoveVisibility; self.World.ActorRemoved += RemoveVisibility;
self.World.ActorAdded += AddShroudGeneration; self.World.ActorAdded += a => { CPos[] shrouded = null; AddShroudGeneration(a, ref shrouded); };
self.World.ActorRemoved += RemoveShroudGeneration; self.World.ActorRemoved += RemoveShroudGeneration;
fogVisibilities = Exts.Lazy(() => self.TraitsImplementing<IFogVisibilityModifier>().ToArray()); fogVisibilities = Exts.Lazy(() => self.TraitsImplementing<IFogVisibilityModifier>().ToArray());
@@ -98,6 +98,25 @@ namespace OpenRA.Traits
Hash += 1; Hash += 1;
} }
public static void UpdateVisibility(IEnumerable<Shroud> shrouds, Actor actor)
{
CPos[] visbility = null;
foreach (var shroud in shrouds)
shroud.UpdateVisibility(actor, ref visbility);
}
public static void UpdateShroudGeneration(IEnumerable<Shroud> 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<CPos> FindVisibleTiles(World world, CPos position, WRange radius) static IEnumerable<CPos> FindVisibleTiles(World world, CPos position, WRange radius)
{ {
var map = world.Map; var map = world.Map;
@@ -110,17 +129,15 @@ namespace OpenRA.Traits
yield return cell; yield return cell;
} }
void AddVisibility(Actor a) void AddVisibility(Actor a, ref CPos[] visible)
{ {
var rs = a.TraitOrDefault<RevealsShroud>(); var rs = a.TraitOrDefault<RevealsShroud>();
if (rs == null || !a.Owner.IsAlliedWith(self.Owner) || rs.Range == WRange.Zero) if (rs == null || !a.Owner.IsAlliedWith(self.Owner) || rs.Range == WRange.Zero)
return; return;
var origins = GetVisOrigins(a); // Lazily generate the visible tiles, allowing the caller to re-use them if desired.
var visible = origins.SelectMany(o => FindVisibleTiles(a.World, o, rs.Range)) visible = visible ?? FindVisibleTiles(a, rs.Range);
.Distinct().ToArray();
// Update visibility
foreach (var c in visible) foreach (var c in visible)
{ {
visibleCount[c]++; visibleCount[c]++;
@@ -147,24 +164,25 @@ namespace OpenRA.Traits
Invalidate(); Invalidate();
} }
public void UpdateVisibility(Actor a) void UpdateVisibility(Actor a, ref CPos[] visible)
{ {
// Actors outside the world don't have any vis // Actors outside the world don't have any vis
if (!a.IsInWorld) if (!a.IsInWorld)
return; return;
RemoveVisibility(a); RemoveVisibility(a);
AddVisibility(a); AddVisibility(a, ref visible);
} }
void AddShroudGeneration(Actor a) void AddShroudGeneration(Actor a, ref CPos[] shrouded)
{ {
var cs = a.TraitOrDefault<CreatesShroud>(); var cs = a.TraitOrDefault<CreatesShroud>();
if (cs == null || a.Owner.IsAlliedWith(self.Owner) || cs.Range == WRange.Zero) if (cs == null || a.Owner.IsAlliedWith(self.Owner) || cs.Range == WRange.Zero)
return; return;
var shrouded = GetVisOrigins(a).SelectMany(o => FindVisibleTiles(a.World, o, cs.Range)) // Lazily generate the shrouded tiles, allowing the caller to re-use them if desired.
.Distinct().ToArray(); shrouded = shrouded ?? FindVisibleTiles(a, cs.Range);
foreach (var c in shrouded) foreach (var c in shrouded)
generatedShroudCount[c]++; generatedShroudCount[c]++;
@@ -188,10 +206,10 @@ namespace OpenRA.Traits
Invalidate(); Invalidate();
} }
public void UpdateShroudGeneration(Actor a) void UpdateShroudGeneration(Actor a, ref CPos[] shrouded)
{ {
RemoveShroudGeneration(a); RemoveShroudGeneration(a);
AddShroudGeneration(a); AddShroudGeneration(a, ref shrouded);
} }
public void UpdatePlayerStance(World w, Player player, Stance oldStance, Stance newStance) 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)) foreach (var a in w.Actors.Where(a => a.Owner == player))
{ {
UpdateVisibility(a); CPos[] visible = null;
UpdateShroudGeneration(a); UpdateVisibility(a, ref visible);
CPos[] shrouded = null;
UpdateShroudGeneration(a, ref shrouded);
} }
} }