Further optimize atmos by reusing specific arrays instead of using a shared arraypool

This commit is contained in:
Vera Aguilera Puerto
2021-07-24 16:33:04 +02:00
parent e83bf9ef34
commit d8fe13af04
2 changed files with 51 additions and 54 deletions

View File

@@ -145,7 +145,6 @@ namespace Content.Server.Atmos.EntitySystems
{ {
foreach (var fireAct in entity.GetAllComponents<IFireAct>()) foreach (var fireAct in entity.GetAllComponents<IFireAct>())
{ {
fireAct.FireAct(tile.Hotspot.Temperature, tile.Hotspot.Volume); fireAct.FireAct(tile.Hotspot.Temperature, tile.Hotspot.Volume);
} }
} }

View File

@@ -20,6 +20,14 @@ namespace Content.Server.Atmos.EntitySystems
private readonly TileAtmosphereComparer _monstermosComparer = new(); private readonly TileAtmosphereComparer _monstermosComparer = new();
private readonly TileAtmosphere[] _equalizeTiles = new TileAtmosphere[Atmospherics.MonstermosHardTileLimit];
private readonly TileAtmosphere[] _equalizeGiverTiles = new TileAtmosphere[Atmospherics.MonstermosTileLimit];
private readonly TileAtmosphere[] _equalizeTakerTiles = new TileAtmosphere[Atmospherics.MonstermosTileLimit];
private readonly TileAtmosphere[] _equalizeQueue = new TileAtmosphere[Atmospherics.MonstermosTileLimit];
private readonly TileAtmosphere[] _depressurizeTiles = new TileAtmosphere[Atmospherics.MonstermosHardTileLimit];
private readonly TileAtmosphere[] _depressurizeSpaceTiles = new TileAtmosphere[Atmospherics.MonstermosHardTileLimit];
private readonly TileAtmosphere[] _depressurizeProgressionOrder = new TileAtmosphere[Atmospherics.MonstermosHardTileLimit * 2];
public void EqualizePressureInZone(IMapGrid mapGrid, GridAtmosphereComponent gridAtmosphere, TileAtmosphere tile, int cycleNum) public void EqualizePressureInZone(IMapGrid mapGrid, GridAtmosphereComponent gridAtmosphere, TileAtmosphere tile, int cycleNum)
{ {
if (tile.Air == null || (tile.MonstermosInfo.LastCycle >= cycleNum)) if (tile.Air == null || (tile.MonstermosInfo.LastCycle >= cycleNum))
@@ -51,14 +59,13 @@ namespace Content.Server.Atmos.EntitySystems
var queueCycle = ++gridAtmosphere.EqualizationQueueCycleControl; var queueCycle = ++gridAtmosphere.EqualizationQueueCycleControl;
var totalMoles = 0f; var totalMoles = 0f;
var tiles = ArrayPool<TileAtmosphere>.Shared.Rent(Atmospherics.MonstermosHardTileLimit); _equalizeTiles[0] = tile;
tiles[0] = tile;
tile.MonstermosInfo.LastQueueCycle = queueCycle; tile.MonstermosInfo.LastQueueCycle = queueCycle;
var tileCount = 1; var tileCount = 1;
for (var i = 0; i < tileCount; i++) for (var i = 0; i < tileCount; i++)
{ {
if (i > Atmospherics.MonstermosHardTileLimit) break; if (i > Atmospherics.MonstermosHardTileLimit) break;
var exploring = tiles[i]; var exploring = _equalizeTiles[i];
if (i < Atmospherics.MonstermosTileLimit) if (i < Atmospherics.MonstermosTileLimit)
{ {
@@ -77,7 +84,7 @@ namespace Content.Server.Atmos.EntitySystems
adj.MonstermosInfo = new MonstermosInfo {LastQueueCycle = queueCycle}; adj.MonstermosInfo = new MonstermosInfo {LastQueueCycle = queueCycle};
if(tileCount < Atmospherics.MonstermosHardTileLimit) if(tileCount < Atmospherics.MonstermosHardTileLimit)
tiles[tileCount++] = adj; _equalizeTiles[tileCount++] = adj;
if (adj.Air.Immutable) if (adj.Air.Immutable)
{ {
@@ -94,35 +101,33 @@ namespace Content.Server.Atmos.EntitySystems
for (var i = Atmospherics.MonstermosTileLimit; i < tileCount; i++) for (var i = Atmospherics.MonstermosTileLimit; i < tileCount; i++)
{ {
//We unmark them. We shouldn't be pushing/pulling gases to/from them. //We unmark them. We shouldn't be pushing/pulling gases to/from them.
var otherTile = tiles[i]; var otherTile = _equalizeTiles[i];
if (otherTile == null) if (otherTile == null)
continue; continue;
tiles[i].MonstermosInfo.LastQueueCycle = 0; _equalizeTiles[i].MonstermosInfo.LastQueueCycle = 0;
} }
tileCount = Atmospherics.MonstermosTileLimit; tileCount = Atmospherics.MonstermosTileLimit;
} }
var averageMoles = totalMoles / (tileCount); var averageMoles = totalMoles / (tileCount);
var giverTiles = ArrayPool<TileAtmosphere>.Shared.Rent(tileCount);
var takerTiles = ArrayPool<TileAtmosphere>.Shared.Rent(tileCount);
var giverTilesLength = 0; var giverTilesLength = 0;
var takerTilesLength = 0; var takerTilesLength = 0;
for (var i = 0; i < tileCount; i++) for (var i = 0; i < tileCount; i++)
{ {
var otherTile = tiles[i]; var otherTile = _equalizeTiles[i];
otherTile.MonstermosInfo.LastCycle = cycleNum; otherTile.MonstermosInfo.LastCycle = cycleNum;
otherTile.MonstermosInfo.MoleDelta -= averageMoles; otherTile.MonstermosInfo.MoleDelta -= averageMoles;
if (otherTile.MonstermosInfo.MoleDelta > 0) if (otherTile.MonstermosInfo.MoleDelta > 0)
{ {
giverTiles[giverTilesLength++] = otherTile; _equalizeGiverTiles[giverTilesLength++] = otherTile;
} }
else else
{ {
takerTiles[takerTilesLength++] = otherTile; _equalizeTakerTiles[takerTilesLength++] = otherTile;
} }
} }
@@ -132,11 +137,11 @@ namespace Content.Server.Atmos.EntitySystems
if (giverTilesLength > logN && takerTilesLength > logN) if (giverTilesLength > logN && takerTilesLength > logN)
{ {
// Even if it fails, it will speed up the next part. // Even if it fails, it will speed up the next part.
Array.Sort(tiles, 0, tileCount, _monstermosComparer); Array.Sort(_equalizeTiles, 0, tileCount, _monstermosComparer);
for (var i = 0; i < tileCount; i++) for (var i = 0; i < tileCount; i++)
{ {
var otherTile = tiles[i]; var otherTile = _equalizeTiles[i];
otherTile.MonstermosInfo.FastDone = true; otherTile.MonstermosInfo.FastDone = true;
if (!(otherTile.MonstermosInfo.MoleDelta > 0)) continue; if (!(otherTile.MonstermosInfo.MoleDelta > 0)) continue;
var eligibleDirections = AtmosDirection.Invalid; var eligibleDirections = AtmosDirection.Invalid;
@@ -175,14 +180,14 @@ namespace Content.Server.Atmos.EntitySystems
for (var i = 0; i < tileCount; i++) for (var i = 0; i < tileCount; i++)
{ {
var otherTile = tiles[i]; var otherTile = _equalizeTiles[i];
if (otherTile.MonstermosInfo.MoleDelta > 0) if (otherTile.MonstermosInfo.MoleDelta > 0)
{ {
giverTiles[giverTilesLength++] = otherTile; _equalizeGiverTiles[giverTilesLength++] = otherTile;
} }
else else
{ {
takerTiles[takerTilesLength++] = otherTile; _equalizeTakerTiles[takerTilesLength++] = otherTile;
} }
} }
} }
@@ -191,22 +196,21 @@ namespace Content.Server.Atmos.EntitySystems
if (giverTilesLength < takerTilesLength) if (giverTilesLength < takerTilesLength)
{ {
// as an optimization, we choose one of two methods based on which list is smaller. We really want to avoid O(n^2) if we can. // as an optimization, we choose one of two methods based on which list is smaller. We really want to avoid O(n^2) if we can.
var queue = ArrayPool<TileAtmosphere>.Shared.Rent(tileCount);
for (var j = 0; j < giverTilesLength; j++) for (var j = 0; j < giverTilesLength; j++)
{ {
var giver = giverTiles[j]; var giver = _equalizeGiverTiles[j];
giver.MonstermosInfo.CurrentTransferDirection = AtmosDirection.Invalid; giver.MonstermosInfo.CurrentTransferDirection = AtmosDirection.Invalid;
giver.MonstermosInfo.CurrentTransferAmount = 0; giver.MonstermosInfo.CurrentTransferAmount = 0;
var queueCycleSlow = ++gridAtmosphere.EqualizationQueueCycleControl; var queueCycleSlow = ++gridAtmosphere.EqualizationQueueCycleControl;
var queueLength = 0; var queueLength = 0;
queue[queueLength++] = giver; _equalizeQueue[queueLength++] = giver;
giver.MonstermosInfo.LastSlowQueueCycle = queueCycleSlow; giver.MonstermosInfo.LastSlowQueueCycle = queueCycleSlow;
for (var i = 0; i < queueLength; i++) for (var i = 0; i < queueLength; i++)
{ {
if (giver.MonstermosInfo.MoleDelta <= 0) if (giver.MonstermosInfo.MoleDelta <= 0)
break; // We're done here now. Let's not do more work than needed. break; // We're done here now. Let's not do more work than needed.
var otherTile = queue[i]; var otherTile = _equalizeQueue[i];
for (var k = 0; k < Atmospherics.Directions; k++) for (var k = 0; k < Atmospherics.Directions; k++)
{ {
var direction = (AtmosDirection) (1 << k); var direction = (AtmosDirection) (1 << k);
@@ -216,7 +220,7 @@ namespace Content.Server.Atmos.EntitySystems
if (otherTile2 == null || otherTile2.MonstermosInfo.LastQueueCycle != queueCycle) continue; if (otherTile2 == null || otherTile2.MonstermosInfo.LastQueueCycle != queueCycle) continue;
if (otherTile2.MonstermosInfo.LastSlowQueueCycle == queueCycleSlow) continue; if (otherTile2.MonstermosInfo.LastSlowQueueCycle == queueCycleSlow) continue;
queue[queueLength++] = otherTile2; _equalizeQueue[queueLength++] = otherTile2;
otherTile2.MonstermosInfo.LastSlowQueueCycle = queueCycleSlow; otherTile2.MonstermosInfo.LastSlowQueueCycle = queueCycleSlow;
otherTile2.MonstermosInfo.CurrentTransferDirection = direction.GetOpposite(); otherTile2.MonstermosInfo.CurrentTransferDirection = direction.GetOpposite();
otherTile2.MonstermosInfo.CurrentTransferAmount = 0; otherTile2.MonstermosInfo.CurrentTransferAmount = 0;
@@ -244,7 +248,7 @@ namespace Content.Server.Atmos.EntitySystems
// Putting this loop here helps make it O(n^2) over O(n^3) // Putting this loop here helps make it O(n^2) over O(n^3)
for (var i = queueLength - 1; i >= 0; i--) for (var i = queueLength - 1; i >= 0; i--)
{ {
var otherTile = queue[i]; var otherTile = _equalizeQueue[i];
if (otherTile.MonstermosInfo.CurrentTransferAmount != 0 && otherTile.MonstermosInfo.CurrentTransferDirection != AtmosDirection.Invalid) if (otherTile.MonstermosInfo.CurrentTransferAmount != 0 && otherTile.MonstermosInfo.CurrentTransferDirection != AtmosDirection.Invalid)
{ {
AdjustEqMovement(otherTile, otherTile.MonstermosInfo.CurrentTransferDirection, otherTile.MonstermosInfo.CurrentTransferAmount); AdjustEqMovement(otherTile, otherTile.MonstermosInfo.CurrentTransferDirection, otherTile.MonstermosInfo.CurrentTransferAmount);
@@ -254,27 +258,24 @@ namespace Content.Server.Atmos.EntitySystems
} }
} }
} }
ArrayPool<TileAtmosphere>.Shared.Return(queue);
} }
else else
{ {
var queue = ArrayPool<TileAtmosphere>.Shared.Rent(tileCount);
for (var j = 0; j < takerTilesLength; j++) for (var j = 0; j < takerTilesLength; j++)
{ {
var taker = takerTiles[j]; var taker = _equalizeTakerTiles[j];
taker.MonstermosInfo.CurrentTransferDirection = AtmosDirection.Invalid; taker.MonstermosInfo.CurrentTransferDirection = AtmosDirection.Invalid;
taker.MonstermosInfo.CurrentTransferAmount = 0; taker.MonstermosInfo.CurrentTransferAmount = 0;
var queueCycleSlow = ++gridAtmosphere.EqualizationQueueCycleControl; var queueCycleSlow = ++gridAtmosphere.EqualizationQueueCycleControl;
var queueLength = 0; var queueLength = 0;
queue[queueLength++] = taker; _equalizeQueue[queueLength++] = taker;
taker.MonstermosInfo.LastSlowQueueCycle = queueCycleSlow; taker.MonstermosInfo.LastSlowQueueCycle = queueCycleSlow;
for (var i = 0; i < queueLength; i++) for (var i = 0; i < queueLength; i++)
{ {
if (taker.MonstermosInfo.MoleDelta >= 0) if (taker.MonstermosInfo.MoleDelta >= 0)
break; // We're done here now. Let's not do more work than needed. break; // We're done here now. Let's not do more work than needed.
var otherTile = queue[i]; var otherTile = _equalizeQueue[i];
for (var k = 0; k < Atmospherics.Directions; k++) for (var k = 0; k < Atmospherics.Directions; k++)
{ {
var direction = (AtmosDirection) (1 << k); var direction = (AtmosDirection) (1 << k);
@@ -284,7 +285,7 @@ namespace Content.Server.Atmos.EntitySystems
if (taker.MonstermosInfo.MoleDelta >= 0) break; // We're done here now. Let's not do more work than needed. if (taker.MonstermosInfo.MoleDelta >= 0) break; // We're done here now. Let's not do more work than needed.
if (otherTile2 == null || otherTile2.MonstermosInfo.LastQueueCycle != queueCycle) continue; if (otherTile2 == null || otherTile2.MonstermosInfo.LastQueueCycle != queueCycle) continue;
if (otherTile2.MonstermosInfo.LastSlowQueueCycle == queueCycleSlow) continue; if (otherTile2.MonstermosInfo.LastSlowQueueCycle == queueCycleSlow) continue;
queue[queueLength++] = otherTile2; _equalizeQueue[queueLength++] = otherTile2;
otherTile2.MonstermosInfo.LastSlowQueueCycle = queueCycleSlow; otherTile2.MonstermosInfo.LastSlowQueueCycle = queueCycleSlow;
otherTile2.MonstermosInfo.CurrentTransferDirection = direction.GetOpposite(); otherTile2.MonstermosInfo.CurrentTransferDirection = direction.GetOpposite();
otherTile2.MonstermosInfo.CurrentTransferAmount = 0; otherTile2.MonstermosInfo.CurrentTransferAmount = 0;
@@ -312,7 +313,7 @@ namespace Content.Server.Atmos.EntitySystems
for (var i = queueLength - 1; i >= 0; i--) for (var i = queueLength - 1; i >= 0; i--)
{ {
var otherTile = queue[i]; var otherTile = _equalizeQueue[i];
if (otherTile.MonstermosInfo.CurrentTransferAmount == 0 || otherTile.MonstermosInfo.CurrentTransferDirection == AtmosDirection.Invalid) if (otherTile.MonstermosInfo.CurrentTransferAmount == 0 || otherTile.MonstermosInfo.CurrentTransferDirection == AtmosDirection.Invalid)
continue; continue;
@@ -323,19 +324,17 @@ namespace Content.Server.Atmos.EntitySystems
otherTile.MonstermosInfo.CurrentTransferAmount = 0; otherTile.MonstermosInfo.CurrentTransferAmount = 0;
} }
} }
ArrayPool<TileAtmosphere>.Shared.Return(queue);
} }
for (var i = 0; i < tileCount; i++) for (var i = 0; i < tileCount; i++)
{ {
var otherTile = tiles[i]; var otherTile = _equalizeTiles[i];
FinalizeEq(gridAtmosphere, otherTile); FinalizeEq(gridAtmosphere, otherTile);
} }
for (var i = 0; i < tileCount; i++) for (var i = 0; i < tileCount; i++)
{ {
var otherTile = tiles[i]; var otherTile = _equalizeTiles[i];
for (var j = 0; j < Atmospherics.Directions; j++) for (var j = 0; j < Atmospherics.Directions; j++)
{ {
var direction = (AtmosDirection) (1 << j); var direction = (AtmosDirection) (1 << j);
@@ -347,9 +346,11 @@ namespace Content.Server.Atmos.EntitySystems
} }
} }
ArrayPool<TileAtmosphere>.Shared.Return(tiles); // We do cleanup.
ArrayPool<TileAtmosphere>.Shared.Return(giverTiles); Array.Clear(_equalizeTiles, 0, Atmospherics.MonstermosHardTileLimit);
ArrayPool<TileAtmosphere>.Shared.Return(takerTiles); Array.Clear(_equalizeGiverTiles, 0, Atmospherics.MonstermosTileLimit);
Array.Clear(_equalizeTakerTiles, 0, Atmospherics.MonstermosTileLimit);
Array.Clear(_equalizeQueue, 0, Atmospherics.MonstermosTileLimit);
} }
public void ExplosivelyDepressurize(IMapGrid mapGrid, GridAtmosphereComponent gridAtmosphere, TileAtmosphere tile, int cycleNum) public void ExplosivelyDepressurize(IMapGrid mapGrid, GridAtmosphereComponent gridAtmosphere, TileAtmosphere tile, int cycleNum)
@@ -362,24 +363,22 @@ namespace Content.Server.Atmos.EntitySystems
var totalGasesRemoved = 0f; var totalGasesRemoved = 0f;
var queueCycle = ++gridAtmosphere.EqualizationQueueCycleControl; var queueCycle = ++gridAtmosphere.EqualizationQueueCycleControl;
var tiles = ArrayPool<TileAtmosphere>.Shared.Rent(limit);
var spaceTiles = ArrayPool<TileAtmosphere>.Shared.Rent(limit);
var tileCount = 0; var tileCount = 0;
var spaceTileCount = 0; var spaceTileCount = 0;
tiles[tileCount++] = tile; _depressurizeTiles[tileCount++] = tile;
tile.MonstermosInfo = new MonstermosInfo {LastQueueCycle = queueCycle}; tile.MonstermosInfo = new MonstermosInfo {LastQueueCycle = queueCycle};
for (var i = 0; i < tileCount; i++) for (var i = 0; i < tileCount; i++)
{ {
var otherTile = tiles[i]; var otherTile = _depressurizeTiles[i];
otherTile.MonstermosInfo.LastCycle = cycleNum; otherTile.MonstermosInfo.LastCycle = cycleNum;
otherTile.MonstermosInfo.CurrentTransferDirection = AtmosDirection.Invalid; otherTile.MonstermosInfo.CurrentTransferDirection = AtmosDirection.Invalid;
if (otherTile.Air.Immutable) if (otherTile.Air.Immutable)
{ {
spaceTiles[spaceTileCount++] = otherTile; _depressurizeSpaceTiles[spaceTileCount++] = otherTile;
otherTile.PressureSpecificTarget = otherTile; otherTile.PressureSpecificTarget = otherTile;
} }
else else
@@ -397,7 +396,7 @@ namespace Content.Server.Atmos.EntitySystems
// The firelocks might have closed on us. // The firelocks might have closed on us.
if (!otherTile.AdjacentBits.IsFlagSet(direction)) continue; if (!otherTile.AdjacentBits.IsFlagSet(direction)) continue;
otherTile2.MonstermosInfo = new MonstermosInfo {LastQueueCycle = queueCycle}; otherTile2.MonstermosInfo = new MonstermosInfo {LastQueueCycle = queueCycle};
tiles[tileCount++] = otherTile2; _depressurizeTiles[tileCount++] = otherTile2;
} }
} }
@@ -406,20 +405,19 @@ namespace Content.Server.Atmos.EntitySystems
} }
var queueCycleSlow = ++gridAtmosphere.EqualizationQueueCycleControl; var queueCycleSlow = ++gridAtmosphere.EqualizationQueueCycleControl;
var progressionOrder = ArrayPool<TileAtmosphere>.Shared.Rent(limit * 2);
var progressionCount = 0; var progressionCount = 0;
for (var i = 0; i < spaceTileCount; i++) for (var i = 0; i < spaceTileCount; i++)
{ {
var otherTile = spaceTiles[i]; var otherTile = _depressurizeSpaceTiles[i];
progressionOrder[progressionCount++] = otherTile; _depressurizeProgressionOrder[progressionCount++] = otherTile;
otherTile.MonstermosInfo.LastSlowQueueCycle = queueCycleSlow; otherTile.MonstermosInfo.LastSlowQueueCycle = queueCycleSlow;
otherTile.MonstermosInfo.CurrentTransferDirection = AtmosDirection.Invalid; otherTile.MonstermosInfo.CurrentTransferDirection = AtmosDirection.Invalid;
} }
for (var i = 0; i < progressionCount; i++) for (var i = 0; i < progressionCount; i++)
{ {
var otherTile = progressionOrder[i]; var otherTile = _depressurizeProgressionOrder[i];
for (var j = 0; j < Atmospherics.Directions; j++) for (var j = 0; j < Atmospherics.Directions; j++)
{ {
var direction = (AtmosDirection) (1 << j); var direction = (AtmosDirection) (1 << j);
@@ -433,13 +431,13 @@ namespace Content.Server.Atmos.EntitySystems
tile2.MonstermosInfo.CurrentTransferAmount = 0; tile2.MonstermosInfo.CurrentTransferAmount = 0;
tile2.PressureSpecificTarget = otherTile.PressureSpecificTarget; tile2.PressureSpecificTarget = otherTile.PressureSpecificTarget;
tile2.MonstermosInfo.LastSlowQueueCycle = queueCycleSlow; tile2.MonstermosInfo.LastSlowQueueCycle = queueCycleSlow;
progressionOrder[progressionCount++] = tile2; _depressurizeProgressionOrder[progressionCount++] = tile2;
} }
} }
for (var i = progressionCount - 1; i >= 0; i--) for (var i = progressionCount - 1; i >= 0; i--)
{ {
var otherTile = progressionOrder[i]; var otherTile = _depressurizeProgressionOrder[i];
if (otherTile.MonstermosInfo.CurrentTransferDirection == AtmosDirection.Invalid) continue; if (otherTile.MonstermosInfo.CurrentTransferDirection == AtmosDirection.Invalid) continue;
gridAtmosphere.HighPressureDelta.Add(otherTile); gridAtmosphere.HighPressureDelta.Add(otherTile);
AddActiveTile(gridAtmosphere, otherTile); AddActiveTile(gridAtmosphere, otherTile);
@@ -465,7 +463,7 @@ namespace Content.Server.Atmos.EntitySystems
if (GridImpulse && tileCount > 0) if (GridImpulse && tileCount > 0)
{ {
var direction = ((Vector2)tiles[tileCount - 1].GridIndices - tile.GridIndices).Normalized; var direction = ((Vector2)_depressurizeTiles[tileCount - 1].GridIndices - tile.GridIndices).Normalized;
var gridPhysics = ComponentManager.GetComponent<PhysicsComponent>(mapGrid.GridEntityId); var gridPhysics = ComponentManager.GetComponent<PhysicsComponent>(mapGrid.GridEntityId);
@@ -474,9 +472,9 @@ namespace Content.Server.Atmos.EntitySystems
gridPhysics.ApplyAngularImpulse(Vector2.Cross(tile.GridIndices - gridPhysics.LocalCenter, direction) * totalGasesRemoved); gridPhysics.ApplyAngularImpulse(Vector2.Cross(tile.GridIndices - gridPhysics.LocalCenter, direction) * totalGasesRemoved);
} }
ArrayPool<TileAtmosphere>.Shared.Return(tiles); Array.Clear(_depressurizeTiles, 0, Atmospherics.MonstermosHardTileLimit);
ArrayPool<TileAtmosphere>.Shared.Return(spaceTiles); Array.Clear(_depressurizeSpaceTiles, 0, Atmospherics.MonstermosHardTileLimit);
ArrayPool<TileAtmosphere>.Shared.Return(progressionOrder); Array.Clear(_depressurizeProgressionOrder, 0, Atmospherics.MonstermosHardTileLimit * 2);
} }
private void ConsiderFirelocks(GridAtmosphereComponent gridAtmosphere, TileAtmosphere tile, TileAtmosphere other) private void ConsiderFirelocks(GridAtmosphereComponent gridAtmosphere, TileAtmosphere tile, TileAtmosphere other)