Merge pull request #9058 from RoosterDragon/reduce-alloc

Reduce allocations in the main game loop
This commit is contained in:
Pavel Penev
2015-08-24 02:11:08 +03:00
7 changed files with 105 additions and 65 deletions

View File

@@ -260,8 +260,6 @@ namespace OpenRA
[FieldLoader.Ignore] public ProjectedCellRegion ProjectedCellBounds; [FieldLoader.Ignore] public ProjectedCellRegion ProjectedCellBounds;
[FieldLoader.Ignore] public CellRegion AllCells; [FieldLoader.Ignore] public CellRegion AllCells;
readonly Func<PPos, bool> containsTest;
void AssertExists(string filename) void AssertExists(string filename)
{ {
using (var s = Container.GetContent(filename)) using (var s = Container.GetContent(filename))
@@ -275,8 +273,6 @@ namespace OpenRA
/// </summary> /// </summary>
public Map(TileSet tileset, int width, int height) public Map(TileSet tileset, int width, int height)
{ {
containsTest = Contains;
var size = new Size(width, height); var size = new Size(width, height);
var tileShape = Game.ModData.Manifest.TileShape; var tileShape = Game.ModData.Manifest.TileShape;
var tileRef = new TerrainTile(tileset.Templates.First().Key, (byte)0); var tileRef = new TerrainTile(tileset.Templates.First().Key, (byte)0);
@@ -316,8 +312,6 @@ namespace OpenRA
/// <summary>Initializes a map loaded from disk.</summary> /// <summary>Initializes a map loaded from disk.</summary>
public Map(string path) public Map(string path)
{ {
containsTest = Contains;
Path = path; Path = path;
Container = GlobalFileSystem.OpenPackage(path, null, int.MaxValue); Container = GlobalFileSystem.OpenPackage(path, null, int.MaxValue);
@@ -448,8 +442,6 @@ namespace OpenRA
initializedCellProjection = true; initializedCellProjection = true;
if (MaximumTerrainHeight != 0)
{
cellProjection = new CellLayer<PPos[]>(this); cellProjection = new CellLayer<PPos[]>(this);
inverseCellProjection = new CellLayer<List<MPos>>(this); inverseCellProjection = new CellLayer<List<MPos>>(this);
@@ -465,17 +457,25 @@ namespace OpenRA
foreach (var cell in AllCells) foreach (var cell in AllCells)
UpdateProjection(cell); UpdateProjection(cell);
} }
}
void UpdateProjection(CPos cell) void UpdateProjection(CPos cell)
{ {
MPos uv;
if (MaximumTerrainHeight == 0) if (MaximumTerrainHeight == 0)
{
uv = cell.ToMPos(TileShape);
cellProjection[cell] = new[] { (PPos)uv };
var inverse = inverseCellProjection[uv];
inverse.Clear();
inverse.Add(uv);
return; return;
}
if (!initializedCellProjection) if (!initializedCellProjection)
InitializeCellProjection(); InitializeCellProjection();
var uv = cell.ToMPos(TileShape); uv = cell.ToMPos(TileShape);
// Remove old reverse projection // Remove old reverse projection
foreach (var puv in cellProjection[uv]) foreach (var puv in cellProjection[uv])
@@ -630,6 +630,7 @@ namespace OpenRA
} }
} }
if (MaximumTerrainHeight > 0)
tiles.CellEntryChanged += UpdateProjection; tiles.CellEntryChanged += UpdateProjection;
return tiles; return tiles;
@@ -650,6 +651,7 @@ namespace OpenRA
} }
} }
if (MaximumTerrainHeight > 0)
tiles.CellEntryChanged += UpdateProjection; tiles.CellEntryChanged += UpdateProjection;
return tiles; return tiles;
@@ -755,7 +757,15 @@ namespace OpenRA
// The first check ensures that the cell is within the valid map region, avoiding // The first check ensures that the cell is within the valid map region, avoiding
// potential crashes in deeper code. All CellLayers have the same geometry, and // potential crashes in deeper code. All CellLayers have the same geometry, and
// CustomTerrain is convenient (cellProjection may be null and others are Lazy). // CustomTerrain is convenient (cellProjection may be null and others are Lazy).
return CustomTerrain.Contains(uv) && ProjectedCellsCovering(uv).All(containsTest); return CustomTerrain.Contains(uv) && ContainsAllProjectedCellsCovering(uv);
}
bool ContainsAllProjectedCellsCovering(MPos uv)
{
foreach (var puv in ProjectedCellsCovering(uv))
if (!Contains(puv))
return false;
return true;
} }
public bool Contains(PPos puv) public bool Contains(PPos puv)
@@ -822,10 +832,6 @@ namespace OpenRA
static readonly PPos[] NoProjectedCells = { }; static readonly PPos[] NoProjectedCells = { };
public PPos[] ProjectedCellsCovering(MPos uv) public PPos[] ProjectedCellsCovering(MPos uv)
{ {
// Shortcut for mods that don't use heightmaps
if (MaximumTerrainHeight == 0)
return new[] { (PPos)uv };
if (!initializedCellProjection) if (!initializedCellProjection)
InitializeCellProjection(); InitializeCellProjection();
@@ -835,21 +841,17 @@ namespace OpenRA
return cellProjection[uv]; return cellProjection[uv];
} }
public MPos[] Unproject(PPos puv) public List<MPos> Unproject(PPos puv)
{ {
var uv = (MPos)puv; var uv = (MPos)puv;
// Shortcut for mods that don't use heightmaps
if (MaximumTerrainHeight == 0)
return new[] { uv };
if (!initializedCellProjection) if (!initializedCellProjection)
InitializeCellProjection(); InitializeCellProjection();
if (!inverseCellProjection.Contains(uv)) if (!inverseCellProjection.Contains(uv))
return new MPos[0]; return new List<MPos>();
return inverseCellProjection[uv].ToArray(); return inverseCellProjection[uv];
} }
public int FacingBetween(CPos cell, CPos towards, int fallbackfacing) public int FacingBetween(CPos cell, CPos towards, int fallbackfacing)
@@ -966,7 +968,7 @@ namespace OpenRA
return (MPos)Clamp((PPos)uv); return (MPos)Clamp((PPos)uv);
// Already in bounds, so don't need to do anything. // Already in bounds, so don't need to do anything.
if (ProjectedCellsCovering(uv).Any(containsTest)) if (ContainsAllProjectedCellsCovering(uv))
return uv; return uv;
// Clamping map coordinates is trickier than it might first look! // Clamping map coordinates is trickier than it might first look!
@@ -1028,7 +1030,7 @@ namespace OpenRA
public CPos ChooseRandomCell(MersenneTwister rand) public CPos ChooseRandomCell(MersenneTwister rand)
{ {
MPos[] cells; List<MPos> cells;
do do
{ {
var u = rand.Next(Bounds.Left, Bounds.Right); var u = rand.Next(Bounds.Left, Bounds.Right);
@@ -1093,7 +1095,7 @@ namespace OpenRA
public CPos ChooseRandomEdgeCell(MersenneTwister rand) public CPos ChooseRandomEdgeCell(MersenneTwister rand)
{ {
MPos[] cells; List<MPos> cells;
do do
{ {
var isU = rand.Next(2) == 0; var isU = rand.Next(2) == 0;

View File

@@ -270,7 +270,11 @@ namespace OpenRA.Traits
if (!map.Contains(uv)) if (!map.Contains(uv))
return false; return false;
return map.ProjectedCellsCovering(uv).Any(isExploredTest); foreach (var puv in map.ProjectedCellsCovering(uv))
if (IsExplored(puv))
return true;
return false;
} }
public bool IsExplored(PPos puv) public bool IsExplored(PPos puv)
@@ -316,7 +320,11 @@ namespace OpenRA.Traits
if (!visibleCount.Contains(uv)) if (!visibleCount.Contains(uv))
return false; return false;
return map.ProjectedCellsCovering(uv).Any(isVisibleTest); foreach (var puv in map.ProjectedCellsCovering(uv))
if (IsVisible(puv))
return true;
return false;
} }
// In internal shroud coords // In internal shroud coords

View File

@@ -189,6 +189,7 @@ namespace OpenRA.Mods.Common.AI
public Player Player { get; private set; } public Player Player { get; private set; }
readonly Func<Actor, bool> isEnemyUnit;
Dictionary<SupportPowerInstance, int> waitingPowers = new Dictionary<SupportPowerInstance, int>(); Dictionary<SupportPowerInstance, int> waitingPowers = new Dictionary<SupportPowerInstance, int>();
Dictionary<string, SupportPowerDecision> powerDecisions = new Dictionary<string, SupportPowerDecision>(); Dictionary<string, SupportPowerDecision> powerDecisions = new Dictionary<string, SupportPowerDecision>();
@@ -229,6 +230,8 @@ namespace OpenRA.Mods.Common.AI
{ {
Info = info; Info = info;
World = init.World; World = init.World;
isEnemyUnit = unit =>
Player.Stances[unit.Owner] == Stance.Enemy && !unit.HasTrait<Husk>() && unit.HasTrait<ITargetable>();
foreach (var decision in info.PowerDecisions) foreach (var decision in info.PowerDecisions)
powerDecisions.Add(decision.OrderName, decision); powerDecisions.Add(decision.OrderName, decision);
@@ -580,20 +583,12 @@ namespace OpenRA.Mods.Common.AI
internal Actor FindClosestEnemy(WPos pos) internal Actor FindClosestEnemy(WPos pos)
{ {
var allEnemyUnits = World.Actors return World.Actors.Where(isEnemyUnit).ClosestTo(pos);
.Where(unit => Player.Stances[unit.Owner] == Stance.Enemy && !unit.HasTrait<Husk>() &&
unit.HasTrait<ITargetable>());
return allEnemyUnits.ClosestTo(pos);
} }
internal Actor FindClosestEnemy(WPos pos, WDist radius) internal Actor FindClosestEnemy(WPos pos, WDist radius)
{ {
var enemyUnits = World.FindActorsInCircle(pos, radius) return World.FindActorsInCircle(pos, radius).Where(isEnemyUnit).ClosestTo(pos);
.Where(unit => Player.Stances[unit.Owner] == Stance.Enemy &&
!unit.HasTrait<Husk>() && unit.HasTrait<ITargetable>());
return enemyUnits.ClosestTo(pos);
} }
List<Actor> FindEnemyConstructionYards() List<Actor> FindEnemyConstructionYards()

View File

@@ -40,7 +40,17 @@ namespace OpenRA.Mods.Common.Traits
public class RepairableBuilding : UpgradableTrait<RepairableBuildingInfo>, ITick public class RepairableBuilding : UpgradableTrait<RepairableBuildingInfo>, ITick
{ {
[Sync] [Sync]
public int RepairersHash { get { return Repairers.Aggregate(0, (code, player) => code ^ Sync.HashPlayer(player)); } } public int RepairersHash
{
get
{
var hash = 0;
foreach (var player in Repairers)
hash ^= Sync.HashPlayer(player);
return hash;
}
}
public readonly List<Player> Repairers = new List<Player>(); public readonly List<Player> Repairers = new List<Player>();
readonly Health health; readonly Health health;
@@ -94,7 +104,9 @@ namespace OpenRA.Mods.Common.Traits
Repairers.RemoveAll(isNotActiveAlly); Repairers.RemoveAll(isNotActiveAlly);
// If after the previous operation there's no repairers left, stop // If after the previous operation there's no repairers left, stop
if (!Repairers.Any()) return; if (Repairers.Count == 0)
return;
var buildingValue = self.GetSellValue(); var buildingValue = self.GetSellValue();
// The cost is the same regardless of the amount of people repairing // The cost is the same regardless of the amount of people repairing

View File

@@ -70,6 +70,7 @@ namespace OpenRA.Mods.Common.Traits
readonly HarvesterInfo info; readonly HarvesterInfo info;
readonly Mobile mobile; readonly Mobile mobile;
Dictionary<ResourceTypeInfo, int> contents = new Dictionary<ResourceTypeInfo, int>(); Dictionary<ResourceTypeInfo, int> contents = new Dictionary<ResourceTypeInfo, int>();
bool idleSmart = true;
[Sync] public Actor OwnerLinkedProc = null; [Sync] public Actor OwnerLinkedProc = null;
[Sync] public Actor LastLinkedProc = null; [Sync] public Actor LastLinkedProc = null;
@@ -77,8 +78,17 @@ namespace OpenRA.Mods.Common.Traits
[Sync] int currentUnloadTicks; [Sync] int currentUnloadTicks;
public CPos? LastHarvestedCell = null; public CPos? LastHarvestedCell = null;
public CPos? LastOrderLocation = null; public CPos? LastOrderLocation = null;
[Sync] public int ContentValue { get { return contents.Sum(c => c.Key.ValuePerUnit * c.Value); } } [Sync]
bool idleSmart = true; public int ContentValue
{
get
{
var value = 0;
foreach (var c in contents)
value += c.Key.ValuePerUnit * c.Value;
return value;
}
}
public Harvester(Actor self, HarvesterInfo info) public Harvester(Actor self, HarvesterInfo info)
{ {

View File

@@ -58,7 +58,16 @@ namespace OpenRA.Mods.Common.Traits
public ReadOnlyList<MissionObjective> Objectives; public ReadOnlyList<MissionObjective> Objectives;
[Sync] [Sync]
public int ObjectivesHash { get { return Objectives.Aggregate(0, (code, objective) => code ^ Sync.Hash(objective.State)); } } public int ObjectivesHash
{
get
{
var hash = 0;
foreach (var objective in objectives)
hash ^= Sync.Hash(objective.State);
return hash;
}
}
// This property is used as a flag in 'Cooperative' games to mark that the player has completed all his objectives. // This property is used as a flag in 'Cooperative' games to mark that the player has completed all his objectives.
// The player's WinState is only updated when his allies have all completed their objective as well. // The player's WinState is only updated when his allies have all completed their objective as well.

View File

@@ -68,15 +68,17 @@ namespace OpenRA.Mods.Common.Traits
public readonly ProductionQueueInfo Info; public readonly ProductionQueueInfo Info;
readonly Actor self; readonly Actor self;
// A list of things we could possibly build
readonly Dictionary<ActorInfo, ProductionState> produceable = new Dictionary<ActorInfo, ProductionState>();
readonly List<ProductionItem> queue = new List<ProductionItem>();
readonly IEnumerable<ActorInfo> allProduceables;
readonly IEnumerable<ActorInfo> buildableProduceables;
// Will change if the owner changes // Will change if the owner changes
PowerManager playerPower; PowerManager playerPower;
PlayerResources playerResources; PlayerResources playerResources;
protected DeveloperMode developerMode; protected DeveloperMode developerMode;
// A list of things we could possibly build
Dictionary<ActorInfo, ProductionState> produceable;
List<ProductionItem> queue = new List<ProductionItem>();
public Actor Actor { get { return self; } } public Actor Actor { get { return self; } }
[Sync] public int QueueLength { get { return queue.Count; } } [Sync] public int QueueLength { get { return queue.Count; } }
@@ -101,6 +103,8 @@ namespace OpenRA.Mods.Common.Traits
Enabled = !info.Factions.Any() || info.Factions.Contains(Faction); Enabled = !info.Factions.Any() || info.Factions.Contains(Faction);
CacheProduceables(playerActor); CacheProduceables(playerActor);
allProduceables = produceable.Where(a => a.Value.Buildable || a.Value.Visible).Select(a => a.Key);
buildableProduceables = produceable.Where(a => a.Value.Buildable).Select(a => a.Key);
} }
void ClearQueue() void ClearQueue()
@@ -143,7 +147,7 @@ namespace OpenRA.Mods.Common.Traits
void CacheProduceables(Actor playerActor) void CacheProduceables(Actor playerActor)
{ {
produceable = new Dictionary<ActorInfo, ProductionState>(); produceable.Clear();
if (!Enabled) if (!Enabled)
return; return;
@@ -200,9 +204,9 @@ namespace OpenRA.Mods.Common.Traits
public virtual IEnumerable<ActorInfo> AllItems() public virtual IEnumerable<ActorInfo> AllItems()
{ {
if (self.World.AllowDevCommands && developerMode.AllTech) if (self.World.AllowDevCommands && developerMode.AllTech)
return produceable.Select(a => a.Key); return produceable.Keys;
return produceable.Where(a => a.Value.Buildable || a.Value.Visible).Select(a => a.Key); return allProduceables;
} }
public virtual IEnumerable<ActorInfo> BuildableItems() public virtual IEnumerable<ActorInfo> BuildableItems()
@@ -210,9 +214,9 @@ namespace OpenRA.Mods.Common.Traits
if (!Enabled) if (!Enabled)
return Enumerable.Empty<ActorInfo>(); return Enumerable.Empty<ActorInfo>();
if (self.World.AllowDevCommands && developerMode.AllTech) if (self.World.AllowDevCommands && developerMode.AllTech)
return produceable.Select(a => a.Key); return produceable.Keys;
return produceable.Where(a => a.Value.Buildable).Select(a => a.Key); return buildableProduceables;
} }
public bool CanBuild(ActorInfo actor) public bool CanBuild(ActorInfo actor)