Transparently cache results of GetTerrainIndex in Map.

This method performs an expensive calculation and is called often during pathfinding. We create a cache of the terrain indicies for the map to vastly reduce the cost.
This commit is contained in:
RoosterDragon
2015-08-19 23:48:07 +01:00
parent fa87befeff
commit 4eacb6e5c9
2 changed files with 29 additions and 5 deletions

View File

@@ -246,6 +246,7 @@ namespace OpenRA
[FieldLoader.Ignore] public Lazy<CellLayer<byte>> MapHeight;
[FieldLoader.Ignore] public CellLayer<byte> CustomTerrain;
[FieldLoader.Ignore] CellLayer<short> cachedTerrainIndexes;
[FieldLoader.Ignore] bool initializedCellProjection;
[FieldLoader.Ignore] CellLayer<PPos[]> cellProjection;
@@ -951,9 +952,32 @@ namespace OpenRA
public byte GetTerrainIndex(CPos cell)
{
const short InvalidCachedTerrainIndex = -1;
// Lazily initialize a cache for terrain indexes.
if (cachedTerrainIndexes == null)
{
cachedTerrainIndexes = new CellLayer<short>(this);
cachedTerrainIndexes.Clear(InvalidCachedTerrainIndex);
// Invalidate the entry for a cell if anything could cause the terrain index to change.
Action<CPos> invalidateTerrainIndex = c => cachedTerrainIndexes[c] = InvalidCachedTerrainIndex;
CustomTerrain.CellEntryChanged += invalidateTerrainIndex;
MapTiles.Value.CellEntryChanged += invalidateTerrainIndex;
}
var uv = cell.ToMPos(this);
var custom = CustomTerrain[uv];
return custom != byte.MaxValue ? custom : cachedTileSet.Value.GetTerrainIndex(MapTiles.Value[uv]);
var terrainIndex = cachedTerrainIndexes[uv];
// Cache terrain indexes per cell on demand.
if (terrainIndex == InvalidCachedTerrainIndex)
{
var custom = CustomTerrain[uv];
terrainIndex = cachedTerrainIndexes[uv] =
custom != byte.MaxValue ? custom : cachedTileSet.Value.GetTerrainIndex(MapTiles.Value[uv]);
}
return (byte)terrainIndex;
}
public TerrainTypeInfo GetTerrainInfo(CPos cell)

View File

@@ -74,7 +74,7 @@ namespace OpenRA
public readonly bool PickAny;
public readonly string Category;
TerrainTileInfo[] tileInfo;
readonly TerrainTileInfo[] tileInfo;
public TerrainTemplateInfo(ushort id, string[] images, int2 size, byte[] tiles)
{
@@ -177,7 +177,7 @@ namespace OpenRA
public readonly bool IgnoreTileSpriteOffsets;
[FieldLoader.Ignore]
public readonly Dictionary<ushort, TerrainTemplateInfo> Templates = new Dictionary<ushort, TerrainTemplateInfo>();
public readonly IReadOnlyDictionary<ushort, TerrainTemplateInfo> Templates;
[FieldLoader.Ignore]
public readonly TerrainTypeInfo[] TerrainInfo;
@@ -217,7 +217,7 @@ namespace OpenRA
// Templates
Templates = yaml["Templates"].ToDictionary().Values
.Select(y => new TerrainTemplateInfo(this, y)).ToDictionary(t => t.Id);
.Select(y => new TerrainTemplateInfo(this, y)).ToDictionary(t => t.Id).AsReadOnly();
}
public TileSet(string name, string id, string palette, TerrainTypeInfo[] terrainInfo)