Merge pull request #9058 from RoosterDragon/reduce-alloc
Reduce allocations in the main game loop
This commit is contained in:
@@ -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,34 +442,40 @@ namespace OpenRA
|
|||||||
|
|
||||||
initializedCellProjection = true;
|
initializedCellProjection = true;
|
||||||
|
|
||||||
if (MaximumTerrainHeight != 0)
|
cellProjection = new CellLayer<PPos[]>(this);
|
||||||
|
inverseCellProjection = new CellLayer<List<MPos>>(this);
|
||||||
|
|
||||||
|
// Initialize collections
|
||||||
|
foreach (var cell in AllCells)
|
||||||
{
|
{
|
||||||
cellProjection = new CellLayer<PPos[]>(this);
|
var uv = cell.ToMPos(TileShape);
|
||||||
inverseCellProjection = new CellLayer<List<MPos>>(this);
|
cellProjection[uv] = new PPos[0];
|
||||||
|
inverseCellProjection[uv] = new List<MPos>();
|
||||||
// Initialize collections
|
|
||||||
foreach (var cell in AllCells)
|
|
||||||
{
|
|
||||||
var uv = cell.ToMPos(TileShape);
|
|
||||||
cellProjection[uv] = new PPos[0];
|
|
||||||
inverseCellProjection[uv] = new List<MPos>();
|
|
||||||
}
|
|
||||||
|
|
||||||
// Initialize projections
|
|
||||||
foreach (var cell in AllCells)
|
|
||||||
UpdateProjection(cell);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Initialize projections
|
||||||
|
foreach (var cell in AllCells)
|
||||||
|
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,7 +630,8 @@ namespace OpenRA
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
tiles.CellEntryChanged += UpdateProjection;
|
if (MaximumTerrainHeight > 0)
|
||||||
|
tiles.CellEntryChanged += UpdateProjection;
|
||||||
|
|
||||||
return tiles;
|
return tiles;
|
||||||
}
|
}
|
||||||
@@ -650,7 +651,8 @@ namespace OpenRA
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
tiles.CellEntryChanged += UpdateProjection;
|
if (MaximumTerrainHeight > 0)
|
||||||
|
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;
|
||||||
|
|||||||
@@ -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
|
||||||
|
|||||||
@@ -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()
|
||||||
|
|||||||
@@ -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
|
||||||
|
|||||||
@@ -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)
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -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.
|
||||||
|
|||||||
@@ -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)
|
||||||
|
|||||||
Reference in New Issue
Block a user