From 695d9a6cb1aa8fbc7003cdbdae14db14ef01b97f Mon Sep 17 00:00:00 2001 From: tovl Date: Mon, 25 Nov 2019 19:39:45 +0100 Subject: [PATCH] Centralize shroud changes in one pass to improve performance. --- OpenRA.Game/Graphics/WorldRenderer.cs | 4 +- OpenRA.Game/Traits/Player/FrozenActorLayer.cs | 39 +----- OpenRA.Game/Traits/Player/Shroud.cs | 63 ++++------ OpenRA.Game/Traits/TraitsInterfaces.cs | 2 +- .../Traits/Player/PlayerRadarTerrain.cs | 30 ++--- .../Traits/World/ShroudRenderer.cs | 116 ++++++++++-------- OpenRA.Mods.Common/Widgets/RadarWidget.cs | 68 ++++------ 7 files changed, 123 insertions(+), 199 deletions(-) diff --git a/OpenRA.Game/Graphics/WorldRenderer.cs b/OpenRA.Game/Graphics/WorldRenderer.cs index 987873f3e2..df1a4d0b00 100644 --- a/OpenRA.Game/Graphics/WorldRenderer.cs +++ b/OpenRA.Game/Graphics/WorldRenderer.cs @@ -228,13 +228,11 @@ namespace OpenRA.Graphics if (a.Actor.IsInWorld && !a.Actor.Disposed) a.Trait.RenderAboveWorld(a.Actor, this); - var renderShroud = World.RenderPlayer != null ? World.RenderPlayer.Shroud : null; - if (enableDepthBuffer) Game.Renderer.ClearDepthBuffer(); foreach (var a in World.ActorsWithTrait()) - a.Trait.RenderShroud(renderShroud, this); + a.Trait.RenderShroud(this); if (enableDepthBuffer) Game.Renderer.Context.DisableDepthBuffer(); diff --git a/OpenRA.Game/Traits/Player/FrozenActorLayer.cs b/OpenRA.Game/Traits/Player/FrozenActorLayer.cs index 3d70cda9b3..2e834ac401 100644 --- a/OpenRA.Game/Traits/Player/FrozenActorLayer.cs +++ b/OpenRA.Game/Traits/Player/FrozenActorLayer.cs @@ -215,9 +215,7 @@ namespace OpenRA.Traits readonly Player owner; readonly Dictionary frozenActorsById; readonly SpatiallyPartitioned partitionedFrozenActorIds; - readonly bool[] dirtyBins; readonly HashSet dirtyFrozenActorIds = new HashSet(); - readonly int rows, cols; public FrozenActorLayer(Actor self, FrozenActorLayerInfo info) { @@ -226,24 +224,10 @@ namespace OpenRA.Traits owner = self.Owner; frozenActorsById = new Dictionary(); - // PERF: Partition the map into a series of coarse-grained bins and track changes in the shroud against - // bin - marking that bin dirty if it changes. This is fairly cheap to track and allows us to perform the - // expensive visibility update for frozen actors in these regions. partitionedFrozenActorIds = new SpatiallyPartitioned( world.Map.MapSize.X, world.Map.MapSize.Y, binSize); - cols = world.Map.MapSize.X / binSize + 1; - rows = world.Map.MapSize.Y / binSize + 1; - dirtyBins = new bool[cols * rows]; - self.Trait().CellsChanged += cells => - { - foreach (var cell in cells) - { - var x = cell.U / binSize; - var y = cell.V / binSize; - dirtyBins[y * cols + x] = true; - } - }; + self.Trait().OnShroudChanged += uv => dirtyFrozenActorIds.UnionWith(partitionedFrozenActorIds.At(new int2(uv.U, uv.V))); } public void Add(FrozenActor fa) @@ -285,8 +269,6 @@ namespace OpenRA.Traits void ITick.Tick(Actor self) { - UpdateDirtyFrozenActorsFromDirtyBins(); - var frozenActorsToRemove = new List(); VisibilityHash = 0; FrozenHash = 0; @@ -314,25 +296,6 @@ namespace OpenRA.Traits Remove(fa); } - void UpdateDirtyFrozenActorsFromDirtyBins() - { - // Check which bins on the map were dirtied due to changes in the shroud and gather the frozen actors whose - // footprint overlap with these bins. - for (var y = 0; y < rows; y++) - { - for (var x = 0; x < cols; x++) - { - if (!dirtyBins[y * cols + x]) - continue; - - var box = new Rectangle(x * binSize, y * binSize, binSize, binSize); - dirtyFrozenActorIds.UnionWith(partitionedFrozenActorIds.InBox(box)); - } - } - - Array.Clear(dirtyBins, 0, dirtyBins.Length); - } - public virtual IEnumerable Render(Actor self, WorldRenderer wr) { return world.ScreenMap.RenderableFrozenActorsInBox(owner, wr.Viewport.TopLeft, wr.Viewport.BottomRight) diff --git a/OpenRA.Game/Traits/Player/Shroud.cs b/OpenRA.Game/Traits/Player/Shroud.cs index c0982443d7..37601fcc64 100644 --- a/OpenRA.Game/Traits/Player/Shroud.cs +++ b/OpenRA.Game/Traits/Player/Shroud.cs @@ -68,10 +68,10 @@ namespace OpenRA.Traits public object Create(ActorInitializer init) { return new Shroud(init.Self, this); } } - public class Shroud : ISync, INotifyCreated + public class Shroud : ISync, INotifyCreated, ITick { public enum SourceType : byte { PassiveVisibility, Shroud, Visibility } - public event Action> CellsChanged; + public event Action OnShroudChanged; enum ShroudCellType : byte { Shroud, Fog, Visible } class ShroudSource @@ -98,6 +98,7 @@ namespace OpenRA.Traits readonly CellLayer visibleCount; readonly CellLayer generatedShroudCount; readonly CellLayer explored; + readonly CellLayer touched; // Per-cell cache of the resolved cell type (shroud/fog/visible) readonly CellLayer resolvedType; @@ -117,7 +118,6 @@ namespace OpenRA.Traits return; disabled = value; - Invalidate(map.ProjectedCellBounds); } } @@ -141,6 +141,7 @@ namespace OpenRA.Traits visibleCount = new CellLayer(map); generatedShroudCount = new CellLayer(map); explored = new CellLayer(map); + touched = new CellLayer(map); // Defaults to 0 = Shroud resolvedType = new CellLayer(map); @@ -156,11 +157,19 @@ namespace OpenRA.Traits self.World.AddFrameEndTask(w => ExploreAll()); } - void Invalidate(IEnumerable changed) + void ITick.Tick(Actor self) { - foreach (var puv in changed) + if (OnShroudChanged == null) + return; + + foreach (var puv in map.ProjectedCellBounds) { var uv = (MPos)puv; + if (!touched[uv]) + continue; + + touched[uv] = false; + var type = ShroudCellType.Shroud; if (explored[uv] && (!shroudGenerationEnabled || generatedShroudCount[uv] == 0 || visibleCount[uv] > 0)) @@ -172,18 +181,13 @@ namespace OpenRA.Traits type = count > 0 ? ShroudCellType.Visible : ShroudCellType.Fog; } + var oldResolvedType = resolvedType[uv]; resolvedType[uv] = type; + if (type != oldResolvedType) + OnShroudChanged((PPos)uv); } - if (CellsChanged != null) - CellsChanged(changed); - - var oldHash = Hash; - Hash = Sync.HashPlayer(self.Owner) + self.World.WorldTick * 3; - - // Invalidate may be called multiple times in one world tick, which is decoupled from rendering. - if (oldHash == Hash) - Hash += 1; + Hash = Sync.HashPlayer(self.Owner) + self.World.WorldTick; } public static IEnumerable ProjectedCellsInRange(Map map, WPos pos, WDist minRange, WDist maxRange, int maxHeightDelta = -1) @@ -229,6 +233,7 @@ namespace OpenRA.Traits continue; var uv = (MPos)puv; + touched[uv] = true; switch (type) { case SourceType.PassiveVisibility: @@ -246,8 +251,6 @@ namespace OpenRA.Traits break; } } - - Invalidate(projectedCells); } public void RemoveSource(object key) @@ -262,6 +265,7 @@ namespace OpenRA.Traits if (map.Contains(puv)) { var uv = (MPos)puv; + touched[uv] = true; switch (state.Type) { case SourceType.PassiveVisibility: @@ -278,23 +282,19 @@ namespace OpenRA.Traits } sources.Remove(key); - Invalidate(state.ProjectedCells); } public void ExploreProjectedCells(World world, IEnumerable cells) { - var changed = new HashSet(); foreach (var puv in cells) { var uv = (MPos)puv; if (map.Contains(puv) && !explored[uv]) { + touched[uv] = true; explored[uv] = true; - changed.Add(puv); } } - - Invalidate(changed); } public void Explore(Shroud s) @@ -302,51 +302,38 @@ namespace OpenRA.Traits if (map.Bounds != s.map.Bounds) throw new ArgumentException("The map bounds of these shrouds do not match.", "s"); - var changed = new List(); foreach (var puv in map.ProjectedCellBounds) { var uv = (MPos)puv; if (!explored[uv] && s.explored[uv]) { + touched[uv] = true; explored[uv] = true; - changed.Add(puv); } } - - Invalidate(changed); } public void ExploreAll() { - var changed = new List(); foreach (var puv in map.ProjectedCellBounds) { var uv = (MPos)puv; if (!explored[uv]) { + touched[uv] = true; explored[uv] = true; - changed.Add(puv); } } - - Invalidate(changed); } public void ResetExploration() { - var changed = new List(); foreach (var puv in map.ProjectedCellBounds) { var uv = (MPos)puv; - var visible = visibleCount[uv] + passiveVisibleCount[uv] > 0; - if (explored[uv] != visible) - { - explored[uv] = visible; - changed.Add(puv); - } + touched[uv] = true; + explored[uv] = (visibleCount[uv] + passiveVisibleCount[uv]) > 0; } - - Invalidate(changed); } public bool IsExplored(WPos pos) diff --git a/OpenRA.Game/Traits/TraitsInterfaces.cs b/OpenRA.Game/Traits/TraitsInterfaces.cs index b6c30b76f1..4e9cb9323b 100644 --- a/OpenRA.Game/Traits/TraitsInterfaces.cs +++ b/OpenRA.Game/Traits/TraitsInterfaces.cs @@ -410,7 +410,7 @@ namespace OpenRA.Traits public interface INotifyIdle { void TickIdle(Actor self); } public interface IRenderAboveWorld { void RenderAboveWorld(Actor self, WorldRenderer wr); } - public interface IRenderShroud { void RenderShroud(Shroud shroud, WorldRenderer wr); } + public interface IRenderShroud { void RenderShroud(WorldRenderer wr); } [RequireExplicitImplementation] public interface IRenderTerrain { void RenderTerrain(WorldRenderer wr, Viewport viewport); } diff --git a/OpenRA.Mods.Common/Traits/Player/PlayerRadarTerrain.cs b/OpenRA.Mods.Common/Traits/Player/PlayerRadarTerrain.cs index 424c263576..b9caa9aa84 100644 --- a/OpenRA.Mods.Common/Traits/Player/PlayerRadarTerrain.cs +++ b/OpenRA.Mods.Common/Traits/Player/PlayerRadarTerrain.cs @@ -31,7 +31,6 @@ namespace OpenRA.Mods.Common.Traits readonly World world; CellLayer> terrainColor; - readonly HashSet dirtyTerrainCells = new HashSet(); readonly Shroud shroud; public event Action CellTerrainColorChanged = null; @@ -40,33 +39,22 @@ namespace OpenRA.Mods.Common.Traits { world = self.World; shroud = self.Trait(); - shroud.CellsChanged += OnShroudCellsChanged; + shroud.OnShroudChanged += UpdateShroudCell; } - void OnShroudCellsChanged(IEnumerable puvs) + void UpdateShroudCell(PPos puv) { - foreach (var puv in puvs) - { - foreach (var uv in world.Map.Unproject(puv)) - { - if (dirtyTerrainCells.Contains(uv)) - { - UpdateTerrainCellColor(uv); - dirtyTerrainCells.Remove(uv); - } - } - } + var uvs = world.Map.Unproject(puv); + foreach (var uv in uvs) + UpdateTerrainCell(uv); } - void UpdateTerrainCell(CPos cell) + void UpdateTerrainCell(MPos uv) { - var uv = cell.ToMPos(world.Map); if (!world.Map.CustomTerrain.Contains(uv)) return; - if (!shroud.IsVisible(uv)) - dirtyTerrainCells.Add(uv); - else + if (shroud.IsVisible(uv)) UpdateTerrainCellColor(uv); } @@ -88,8 +76,8 @@ namespace OpenRA.Mods.Common.Traits foreach (var uv in world.Map.AllCells.MapCoords) UpdateTerrainCellColor(uv); - world.Map.Tiles.CellEntryChanged += UpdateTerrainCell; - world.Map.CustomTerrain.CellEntryChanged += UpdateTerrainCell; + world.Map.Tiles.CellEntryChanged += cell => UpdateTerrainCell(cell.ToMPos(world.Map)); + world.Map.CustomTerrain.CellEntryChanged += cell => UpdateTerrainCell(cell.ToMPos(world.Map)); IsInitialized = true; }); diff --git a/OpenRA.Mods.Common/Traits/World/ShroudRenderer.cs b/OpenRA.Mods.Common/Traits/World/ShroudRenderer.cs index 7384e5f382..c674889f7f 100644 --- a/OpenRA.Mods.Common/Traits/World/ShroudRenderer.cs +++ b/OpenRA.Mods.Common/Traits/World/ShroudRenderer.cs @@ -14,6 +14,7 @@ using System.Collections.Generic; using System.IO; using System.Linq; using OpenRA.Graphics; +using OpenRA.Primitives; using OpenRA.Traits; namespace OpenRA.Mods.Common.Traits @@ -92,17 +93,17 @@ namespace OpenRA.Mods.Common.Traits } readonly ShroudRendererInfo info; + readonly World world; readonly Map map; readonly Edges notVisibleEdges; readonly byte variantStride; readonly byte[] edgesToSpriteIndexOffset; readonly CellLayer tileInfos; + readonly CellLayer cellsDirty; readonly Sprite[] fogSprites, shroudSprites; - readonly HashSet cellsDirty = new HashSet(); - readonly HashSet cellsAndNeighborsDirty = new HashSet(); - Shroud currentShroud; + Shroud shroud; Func visibleUnderShroud, visibleUnderFog; TerrainSpriteLayer shroudLayer, fogLayer; bool disposed; @@ -122,10 +123,13 @@ namespace OpenRA.Mods.Common.Traits throw new ArgumentException("ShroudRenderer cannot define this many indexes for shroud directions.", "info"); this.info = info; + this.world = world; map = world.Map; tileInfos = new CellLayer(map); + cellsDirty = new CellLayer(map); + // Load sprite variants var variantCount = info.ShroudVariants.Length; variantStride = (byte)(info.Index.Length + (info.OverrideFullShroud != null ? 1 : 0)); @@ -135,12 +139,12 @@ namespace OpenRA.Mods.Common.Traits var sequenceProvider = map.Rules.Sequences; for (var j = 0; j < variantCount; j++) { - var shroud = sequenceProvider.GetSequence(info.Sequence, info.ShroudVariants[j]); - var fog = sequenceProvider.GetSequence(info.Sequence, info.FogVariants[j]); + var shroudSequence = sequenceProvider.GetSequence(info.Sequence, info.ShroudVariants[j]); + var fogSequence = sequenceProvider.GetSequence(info.Sequence, info.FogVariants[j]); for (var i = 0; i < info.Index.Length; i++) { - shroudSprites[j * variantStride + i] = shroud.GetSprite(i); - fogSprites[j * variantStride + i] = fog.GetSprite(i); + shroudSprites[j * variantStride + i] = shroudSequence.GetSprite(i); + fogSprites[j * variantStride + i] = fogSequence.GetSprite(i); } if (info.OverrideFullShroud != null) @@ -160,6 +164,8 @@ namespace OpenRA.Mods.Common.Traits edgesToSpriteIndexOffset[info.OverrideShroudIndex] = (byte)(variantStride - 1); notVisibleEdges = info.UseExtendedIndex ? Edges.AllSides : Edges.AllCorners; + + world.RenderPlayerChanged += WorldOnRenderPlayerChanged; } void IWorldLoaded.WorldLoaded(World w, WorldRenderer wr) @@ -174,9 +180,6 @@ namespace OpenRA.Mods.Common.Traits tileInfos[uv] = new TileInfo(screen, variant); } - // Dirty the whole projected space - DirtyCells(map.AllCells.MapCoords.Select(uv => (PPos)uv)); - // All tiles are visible in the editor if (w.Type == WorldType.Editor) visibleUnderShroud = _ => true; @@ -203,6 +206,8 @@ namespace OpenRA.Mods.Common.Traits shroudLayer = new TerrainSpriteLayer(w, wr, shroudSheet, shroudBlend, wr.Palette(info.ShroudPalette), false); fogLayer = new TerrainSpriteLayer(w, wr, fogSheet, fogBlend, wr.Palette(info.FogPalette), false); + + WorldOnRenderPlayerChanged(world.RenderPlayer); } Edges GetEdges(PPos puv, Func isVisible) @@ -234,54 +239,48 @@ namespace OpenRA.Mods.Common.Traits return info.UseExtendedIndex ? edge ^ ucorner : edge & Edges.AllCorners; } - void DirtyCells(IEnumerable cells) + void WorldOnRenderPlayerChanged(Player player) { - // PERF: Many cells in the shroud change every tick. We only track the changes here and defer the real work - // we need to do until we render. This allows us to avoid wasted work. - cellsDirty.UnionWith(cells); + var newShroud = player != null ? player.Shroud : null; + + if (shroud != newShroud) + { + if (shroud != null) + shroud.OnShroudChanged -= UpdateShroudCell; + + if (newShroud != null) + { + visibleUnderShroud = puv => newShroud.IsExplored(puv); + visibleUnderFog = puv => newShroud.IsVisible(puv); + newShroud.OnShroudChanged += UpdateShroudCell; + } + else + { + visibleUnderShroud = puv => map.Contains(puv); + visibleUnderFog = puv => map.Contains(puv); + } + + shroud = newShroud; + } + + // Dirty the full projected space so the cells outside + // the map bounds can be initialized as fully shrouded. + cellsDirty.Clear(true); + var tl = new PPos(0, 0); + var br = new PPos(map.MapSize.X - 1, map.MapSize.Y - 1); + UpdateShroud(new ProjectedCellRegion(map, tl, br)); } - void IRenderShroud.RenderShroud(Shroud shroud, WorldRenderer wr) + void UpdateShroud(ProjectedCellRegion region) { - if (currentShroud != shroud) - { - if (currentShroud != null) - currentShroud.CellsChanged -= DirtyCells; - - if (shroud != null) - shroud.CellsChanged += DirtyCells; - - // Needs the anonymous function to ensure the correct overload is chosen - if (shroud != null) - visibleUnderShroud = puv => currentShroud.IsExplored(puv); - else - visibleUnderShroud = puv => map.Contains(puv); - - if (shroud != null) - visibleUnderFog = puv => currentShroud.IsVisible(puv); - else - visibleUnderFog = puv => map.Contains(puv); - - currentShroud = shroud; - DirtyCells(map.ProjectedCellBounds); - } - - // We need to update newly dirtied areas of the shroud. - // Expand the dirty area to cover the neighboring cells, since shroud is affected by neighboring cells. - foreach (var uv in cellsDirty) - { - cellsAndNeighborsDirty.Add(uv); - var cell = ((MPos)uv).ToCPos(map); - foreach (var direction in CVec.Directions) - cellsAndNeighborsDirty.Add((PPos)(cell + direction).ToMPos(map)); - } - - foreach (var puv in cellsAndNeighborsDirty) + foreach (var puv in region) { var uv = (MPos)puv; - if (!tileInfos.Contains(uv)) + if (!cellsDirty[uv] || !tileInfos.Contains(uv)) continue; + cellsDirty[uv] = false; + var tileInfo = tileInfos[uv]; var shroudSprite = GetSprite(shroudSprites, GetEdges(puv, visibleUnderShroud), tileInfo.Variant); var shroudPos = tileInfo.ScreenPosition; @@ -296,14 +295,25 @@ namespace OpenRA.Mods.Common.Traits shroudLayer.Update(uv, shroudSprite, shroudPos); fogLayer.Update(uv, fogSprite, fogPos); } + } - cellsDirty.Clear(); - cellsAndNeighborsDirty.Clear(); - + void IRenderShroud.RenderShroud(WorldRenderer wr) + { + UpdateShroud(map.ProjectedCellBounds); fogLayer.Draw(wr.Viewport); shroudLayer.Draw(wr.Viewport); } + void UpdateShroudCell(PPos puv) + { + var uv = (MPos)puv; + cellsDirty[uv] = true; + var cell = uv.ToCPos(map); + foreach (var direction in CVec.Directions) + if (map.Contains((PPos)(cell + direction).ToMPos(map))) + cellsDirty[cell + direction] = true; + } + Sprite GetSprite(Sprite[] sprites, Edges edges, int variant) { if (edges == Edges.None) diff --git a/OpenRA.Mods.Common/Widgets/RadarWidget.cs b/OpenRA.Mods.Common/Widgets/RadarWidget.cs index 5f245a4997..70858fb13a 100644 --- a/OpenRA.Mods.Common/Widgets/RadarWidget.cs +++ b/OpenRA.Mods.Common/Widgets/RadarWidget.cs @@ -39,8 +39,6 @@ namespace OpenRA.Mods.Common.Widgets readonly int previewWidth; readonly int previewHeight; - readonly HashSet dirtyShroudCells = new HashSet(); - float radarMinimapHeight; int frame; bool hasRadar; @@ -56,7 +54,7 @@ namespace OpenRA.Mods.Common.Widgets Sprite terrainSprite; Sprite actorSprite; Sprite shroudSprite; - Shroud renderShroud; + Shroud shroud; PlayerRadarTerrain playerRadarTerrain; Player currentPlayer; @@ -70,7 +68,6 @@ namespace OpenRA.Mods.Common.Widgets this.worldRenderer = worldRenderer; radarPings = world.WorldActor.TraitOrDefault(); - MarkShroudDirty(world.Map.AllCells.MapCoords.Select(uv => (PPos)uv)); isRectangularIsometric = world.Map.Grid.Type == MapGridType.RectangularIsometric; cellWidth = isRectangularIsometric ? 2 : 1; @@ -131,22 +128,21 @@ namespace OpenRA.Mods.Common.Widgets { currentPlayer = player; - var newRenderShroud = player != null ? player.Shroud : null; - if (newRenderShroud != renderShroud) + var newShroud = player != null ? player.Shroud : null; + + if (newShroud != shroud) { - if (renderShroud != null) - renderShroud.CellsChanged -= MarkShroudDirty; + if (shroud != null) + shroud.OnShroudChanged -= UpdateShroudCell; - if (newRenderShroud != null) + if (newShroud != null) { - // Redraw the full shroud sprite - MarkShroudDirty(world.Map.AllCells.MapCoords.Select(uv => (PPos)uv)); - - // Update the notification binding - newRenderShroud.CellsChanged += MarkShroudDirty; + newShroud.OnShroudChanged += UpdateShroudCell; + foreach (var puv in world.Map.ProjectedCellBounds) + UpdateShroudCell(puv); } - renderShroud = newRenderShroud; + shroud = newShroud; } var newPlayerRadarTerrain = @@ -233,14 +229,10 @@ namespace OpenRA.Mods.Common.Widgets void UpdateShroudCell(PPos puv) { var color = 0; - var rp = world.RenderPlayer; - if (rp != null) - { - if (!rp.Shroud.IsExplored(puv)) - color = Color.Black.ToArgb(); - else if (!rp.Shroud.IsVisible(puv)) - color = Color.FromArgb(128, Color.Black).ToArgb(); - } + if (!currentPlayer.Shroud.IsExplored(puv)) + color = Color.Black.ToArgb(); + else if (!currentPlayer.Shroud.IsVisible(puv)) + color = Color.FromArgb(128, Color.Black).ToArgb(); var stride = radarSheet.Size.Width; unsafe @@ -248,32 +240,25 @@ namespace OpenRA.Mods.Common.Widgets fixed (byte* colorBytes = &radarData[0]) { var colors = (int*)colorBytes; - foreach (var uv in world.Map.Unproject(puv)) + foreach (var iuv in world.Map.Unproject(puv)) { if (isRectangularIsometric) { // Odd rows are shifted right by 1px - var dx = uv.V & 1; - if (uv.U + dx > 0) - colors[uv.V * stride + 2 * uv.U + dx - 1 + previewWidth] = color; + var dx = iuv.V & 1; + if (iuv.U + dx > 0) + colors[iuv.V * stride + 2 * iuv.U + dx - 1 + previewWidth] = color; - if (2 * uv.U + dx < stride) - colors[uv.V * stride + 2 * uv.U + dx + previewWidth] = color; + if (2 * iuv.U + dx < stride) + colors[iuv.V * stride + 2 * iuv.U + dx + previewWidth] = color; } else - colors[uv.V * stride + uv.U + previewWidth] = color; + colors[iuv.V * stride + iuv.U + previewWidth] = color; } } } } - void MarkShroudDirty(IEnumerable projectedCellsChanged) - { - // PERF: Many cells in the shroud change every tick. We only track the changes here and defer the real work - // we need to do until we render. This allows us to avoid wasted work. - dirtyShroudCells.UnionWith(projectedCellsChanged); - } - public override string GetCursor(int2 pos) { if (world == null || !hasRadar) @@ -342,13 +327,6 @@ namespace OpenRA.Mods.Common.Widgets if (world == null) return; - if (renderShroud != null) - { - foreach (var cell in dirtyShroudCells) - UpdateShroudCell(cell); - dirtyShroudCells.Clear(); - } - radarSheet.CommitBufferedData(); var o = new float2(mapRect.Location.X, mapRect.Location.Y + world.Map.Bounds.Height * previewScale * (1 - radarMinimapHeight) / 2); @@ -358,7 +336,7 @@ namespace OpenRA.Mods.Common.Widgets rsr.DrawSprite(terrainSprite, o, s); rsr.DrawSprite(actorSprite, o, s); - if (renderShroud != null) + if (shroud != null) rsr.DrawSprite(shroudSprite, o, s); // Draw viewport rect