From 17996dfdfcd2297cc44a3e8b51e62777b01cd0f7 Mon Sep 17 00:00:00 2001 From: Vapre Date: Wed, 21 Oct 2020 22:23:15 +0200 Subject: [PATCH] Shroud, access ProjectedCellLayer by array index. `Shroud.touchedCount` to avoid Tick updates. Shroud, access ProjectedCellLayer by array index over PPos index. Performance improvement. Avoid the multiple PPos to array index conversions in the same method call by calculating the cell layer index once. Background: `Shroud.Tick` and `ProjectedCellLayer.Index(PPos puv)` shows up in profile reports as one of the most expensive methods (9% of CPU time). In `Shroud.Tick` calls `ProjectedCellLayer.Index(PPos puv)` multiple times for the same or different cell layers of the same dimension. Improvement: Benchmark results show an 0.5ms mean improvement in tick time and 0.3 improvement in render time - on a replay map of 1.12 min of play at max speed. Render time: render222052(bleed) render221934(this commit) count 8144.000000 8144.000000 mean 11.410075 11.470100 std 5.004876 4.731463 min 3.450700 3.638400 25% 7.409100 7.015900 50% 12.410600 12.435900 75% 13.998100 14.242900 max 149.036200 149.656500 Tick time: tick_time222043(bleed) tick_time221923(this commit) count 2366.000000 2366.000000 mean 4.762923 4.275833 std 3.240976 3.206362 min 0.263900 1.653600 25% 4.145375 3.668600 50% 4.779350 4.240050 75% 5.232575 4.611775 max 85.751800 87.387100 Shroud.touchedCount to avoid Tick updates if no cells touched. Avoids iterating over all map cells of the `touched` cell layer. Tick time improvement of 40%+ - during at least the first two minutes of gameplay. During the first minutes of a game - out of every 1000 ticks only 10-100 result in the Shroud - of any player - to be touched. For certains player types (Neutral, Creep) less Shroud updates are expected throughout a complete game. Throughout a complete game human/AI players can also have no Shroud touches during certain Ticks. --- OpenRA.Game/Map/ProjectedCellLayer.cs | 15 ++++- OpenRA.Game/Traits/Player/Shroud.cs | 89 ++++++++++++++++++--------- 2 files changed, 73 insertions(+), 31 deletions(-) diff --git a/OpenRA.Game/Map/ProjectedCellLayer.cs b/OpenRA.Game/Map/ProjectedCellLayer.cs index 1cf01cdc62..c95bd1c74e 100644 --- a/OpenRA.Game/Map/ProjectedCellLayer.cs +++ b/OpenRA.Game/Map/ProjectedCellLayer.cs @@ -22,11 +22,24 @@ namespace OpenRA : base(gridType, size) { } // Resolve an array index from map coordinates. - int Index(PPos uv) + public int Index(PPos uv) { return uv.V * Size.Width + uv.U; } + public T this[int index] + { + get + { + return entries[index]; + } + + set + { + entries[index] = value; + } + } + /// Gets or sets the layer contents using projected map coordinates. public T this[PPos uv] { diff --git a/OpenRA.Game/Traits/Player/Shroud.cs b/OpenRA.Game/Traits/Player/Shroud.cs index 731624062e..229e06330d 100644 --- a/OpenRA.Game/Traits/Player/Shroud.cs +++ b/OpenRA.Game/Traits/Player/Shroud.cs @@ -99,6 +99,7 @@ namespace OpenRA.Traits readonly ProjectedCellLayer generatedShroudCount; readonly ProjectedCellLayer explored; readonly ProjectedCellLayer touched; + bool anyCellTouched; // Per-cell cache of the resolved cell type (shroud/fog/visible) readonly ProjectedCellLayer resolvedType; @@ -142,6 +143,7 @@ namespace OpenRA.Traits generatedShroudCount = new ProjectedCellLayer(map); explored = new ProjectedCellLayer(map); touched = new ProjectedCellLayer(map); + anyCellTouched = true; // Defaults to 0 = Shroud resolvedType = new ProjectedCellLayer(map); @@ -159,31 +161,42 @@ namespace OpenRA.Traits void ITick.Tick(Actor self) { + if (!anyCellTouched) + return; + + anyCellTouched = false; + if (OnShroudChanged == null) return; foreach (var puv in map.ProjectedCells) { - if (!touched[puv]) + var index = touched.Index(puv); + if (!touched[index]) continue; - touched[puv] = false; + touched[index] = false; var type = ShroudCellType.Shroud; - if (explored[puv] && (!shroudGenerationEnabled || generatedShroudCount[puv] == 0 || visibleCount[puv] > 0)) + if (explored[index]) { - var count = visibleCount[puv]; - if (passiveVisibilityEnabled) - count += passiveVisibleCount[puv]; + var count = visibleCount[index]; + if (!shroudGenerationEnabled || count > 0 || generatedShroudCount[index] == 0) + { + if (passiveVisibilityEnabled) + count += passiveVisibleCount[index]; - type = count > 0 ? ShroudCellType.Visible : ShroudCellType.Fog; + type = count > 0 ? ShroudCellType.Visible : ShroudCellType.Fog; + } } - var oldResolvedType = resolvedType[puv]; - resolvedType[puv] = type; + var oldResolvedType = resolvedType[index]; if (type != oldResolvedType) + { + resolvedType[index] = type; OnShroudChanged(puv); + } } Hash = Sync.HashPlayer(self.Owner) + self.World.WorldTick; @@ -231,21 +244,23 @@ namespace OpenRA.Traits if (!map.Contains(puv)) continue; - touched[puv] = true; + var index = touched.Index(puv); + touched[index] = true; + anyCellTouched = true; switch (type) { case SourceType.PassiveVisibility: passiveVisibilityEnabled = true; - passiveVisibleCount[puv]++; - explored[puv] = true; + passiveVisibleCount[index]++; + explored[index] = true; break; case SourceType.Visibility: - visibleCount[puv]++; - explored[puv] = true; + visibleCount[index]++; + explored[index] = true; break; case SourceType.Shroud: shroudGenerationEnabled = true; - generatedShroudCount[puv]++; + generatedShroudCount[index]++; break; } } @@ -261,17 +276,19 @@ namespace OpenRA.Traits // Cells outside the visible bounds don't increment visibleCount if (map.Contains(puv)) { - touched[puv] = true; + var index = touched.Index(puv); + touched[index] = true; + anyCellTouched = true; switch (state.Type) { case SourceType.PassiveVisibility: - passiveVisibleCount[puv]--; + passiveVisibleCount[index]--; break; case SourceType.Visibility: - visibleCount[puv]--; + visibleCount[index]--; break; case SourceType.Shroud: - generatedShroudCount[puv]--; + generatedShroudCount[index]--; break; } } @@ -284,10 +301,15 @@ namespace OpenRA.Traits { foreach (var puv in cells) { - if (map.Contains(puv) && !explored[puv]) + if (map.Contains(puv)) { - touched[puv] = true; - explored[puv] = true; + var index = touched.Index(puv); + if (!explored[index]) + { + touched[index] = true; + anyCellTouched = true; + explored[index] = true; + } } } } @@ -299,10 +321,12 @@ namespace OpenRA.Traits foreach (var puv in map.ProjectedCells) { - if (!explored[puv] && s.explored[puv]) + var index = touched.Index(puv); + if (!explored[index] && s.explored[index]) { - touched[puv] = true; - explored[puv] = true; + touched[index] = true; + anyCellTouched = true; + explored[index] = true; } } } @@ -311,10 +335,12 @@ namespace OpenRA.Traits { foreach (var puv in map.ProjectedCells) { - if (!explored[puv]) + var index = touched.Index(puv); + if (!explored[index]) { - touched[puv] = true; - explored[puv] = true; + touched[index] = true; + anyCellTouched = true; + explored[index] = true; } } } @@ -323,9 +349,12 @@ namespace OpenRA.Traits { foreach (var puv in map.ProjectedCells) { - touched[puv] = true; - explored[puv] = (visibleCount[puv] + passiveVisibleCount[puv]) > 0; + var index = touched.Index(puv); + touched[index] = true; + explored[index] = (visibleCount[index] + passiveVisibleCount[index]) > 0; } + + anyCellTouched = true; } public bool IsExplored(WPos pos)