Merge pull request #5851 from RoosterDragon/shroud-perf
Speed up shroud checks
This commit is contained in:
@@ -36,17 +36,16 @@ namespace OpenRA.Graphics
|
||||
|
||||
unsafe
|
||||
{
|
||||
var c = (int*)bitmapData.Scan0;
|
||||
|
||||
for (var x = 0; x < b.Width; x++)
|
||||
var colors = (int*)bitmapData.Scan0;
|
||||
var stride = bitmapData.Stride / 4;
|
||||
for (var y = 0; y < b.Height; y++)
|
||||
{
|
||||
for (var y = 0; y < b.Height; y++)
|
||||
for (var x = 0; x < b.Width; x++)
|
||||
{
|
||||
var mapX = x + b.Left;
|
||||
var mapY = y + b.Top;
|
||||
var type = tileset.GetTerrainInfo(mapTiles[mapX, mapY]);
|
||||
|
||||
*(c + (y * bitmapData.Stride >> 2) + x) = type.Color.ToArgb();
|
||||
colors[y * stride + x] = type.Color.ToArgb();
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -67,11 +66,11 @@ namespace OpenRA.Graphics
|
||||
|
||||
unsafe
|
||||
{
|
||||
var c = (int*)bitmapData.Scan0;
|
||||
|
||||
for (var x = 0; x < b.Width; x++)
|
||||
var colors = (int*)bitmapData.Scan0;
|
||||
var stride = bitmapData.Stride / 4;
|
||||
for (var y = 0; y < b.Height; y++)
|
||||
{
|
||||
for (var y = 0; y < b.Height; y++)
|
||||
for (var x = 0; x < b.Width; x++)
|
||||
{
|
||||
var mapX = x + b.Left;
|
||||
var mapY = y + b.Top;
|
||||
@@ -85,7 +84,7 @@ namespace OpenRA.Graphics
|
||||
if (res == null)
|
||||
continue;
|
||||
|
||||
*(c + (y * bitmapData.Stride >> 2) + x) = tileset[tileset.GetTerrainIndex(res)].Color.ToArgb();
|
||||
colors[y * stride + x] = tileset[tileset.GetTerrainIndex(res)].Color.ToArgb();
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -107,19 +106,18 @@ namespace OpenRA.Graphics
|
||||
|
||||
unsafe
|
||||
{
|
||||
var c = (int*)bitmapData.Scan0;
|
||||
|
||||
for (var x = 0; x < b.Width; x++)
|
||||
var colors = (int*)bitmapData.Scan0;
|
||||
var stride = bitmapData.Stride / 4;
|
||||
for (var y = 0; y < b.Height; y++)
|
||||
{
|
||||
for (var y = 0; y < b.Height; y++)
|
||||
for (var x = 0; x < b.Width; x++)
|
||||
{
|
||||
var mapX = x + b.Left;
|
||||
var mapY = y + b.Top;
|
||||
var custom = map.CustomTerrain[mapX, mapY];
|
||||
if (custom == -1)
|
||||
continue;
|
||||
|
||||
*(c + (y * bitmapData.Stride >> 2) + x) = world.TileSet[custom].Color.ToArgb();
|
||||
colors[y * stride + x] = world.TileSet[custom].Color.ToArgb();
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -140,8 +138,8 @@ namespace OpenRA.Graphics
|
||||
|
||||
unsafe
|
||||
{
|
||||
var c = (int*)bitmapData.Scan0;
|
||||
|
||||
var colors = (int*)bitmapData.Scan0;
|
||||
var stride = bitmapData.Stride / 4;
|
||||
foreach (var t in world.ActorsWithTrait<IRadarSignature>())
|
||||
{
|
||||
if (world.FogObscures(t.Actor))
|
||||
@@ -152,7 +150,7 @@ namespace OpenRA.Graphics
|
||||
{
|
||||
var uv = Map.CellToMap(map.TileShape, cell);
|
||||
if (b.Contains(uv.X, uv.Y))
|
||||
*(c + ((uv.Y - b.Top) * bitmapData.Stride >> 2) + uv.X - b.Left) = color.ToArgb();
|
||||
colors[(uv.Y - b.Top) * stride + uv.X - b.Left] = color.ToArgb();
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -180,15 +178,17 @@ namespace OpenRA.Graphics
|
||||
|
||||
unsafe
|
||||
{
|
||||
var c = (int*)bitmapData.Scan0;
|
||||
|
||||
var colors = (int*)bitmapData.Scan0;
|
||||
var stride = bitmapData.Stride / 4;
|
||||
var shroudObscured = world.ShroudObscuresTest(map.Cells);
|
||||
var fogObscured = world.FogObscuresTest(map.Cells);
|
||||
foreach (var cell in map.Cells)
|
||||
{
|
||||
var uv = Map.CellToMap(map.TileShape, cell) - offset;
|
||||
if (world.ShroudObscures(cell))
|
||||
*(c + (uv.Y * bitmapData.Stride >> 2) + uv.X) = shroud;
|
||||
else if (world.FogObscures(cell))
|
||||
*(c + (uv.Y * bitmapData.Stride >> 2) + uv.X) = fog;
|
||||
if (shroudObscured(cell))
|
||||
colors[uv.Y * stride + uv.X] = shroud;
|
||||
else if (fogObscured(cell))
|
||||
colors[uv.Y * stride + uv.X] = fog;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -12,7 +12,6 @@ using System;
|
||||
using System.Collections;
|
||||
using System.Collections.Generic;
|
||||
using System.Drawing;
|
||||
using OpenRA.Graphics;
|
||||
|
||||
namespace OpenRA
|
||||
{
|
||||
@@ -21,7 +20,7 @@ namespace OpenRA
|
||||
{
|
||||
public readonly Size Size;
|
||||
public readonly TileShape Shape;
|
||||
T[] entries;
|
||||
readonly T[] entries;
|
||||
|
||||
public CellLayer(Map map)
|
||||
: this(map.TileShape, new Size(map.MapSize.X, map.MapSize.Y)) { }
|
||||
@@ -37,35 +36,27 @@ namespace OpenRA
|
||||
int Index(CPos cell)
|
||||
{
|
||||
var uv = Map.CellToMap(Shape, cell);
|
||||
return uv.Y * Size.Width + uv.X;
|
||||
return Index(uv.X, uv.Y);
|
||||
}
|
||||
|
||||
// Resolve an array index from map coordinates
|
||||
int Index(int u, int v)
|
||||
{
|
||||
return v * Size.Width + u;
|
||||
}
|
||||
|
||||
/// <summary>Gets or sets the <see cref="OpenRA.CellLayer"/> using cell coordinates</summary>
|
||||
public T this[CPos cell]
|
||||
{
|
||||
get
|
||||
{
|
||||
return entries[Index(cell)];
|
||||
}
|
||||
|
||||
set
|
||||
{
|
||||
entries[Index(cell)] = value;
|
||||
}
|
||||
get { return entries[Index(cell)]; }
|
||||
set { entries[Index(cell)] = value; }
|
||||
}
|
||||
|
||||
/// <summary>Gets or sets the layer contents using raw map coordinates (not CPos!)</summary>
|
||||
public T this[int u, int v]
|
||||
{
|
||||
get
|
||||
{
|
||||
return entries[v * Size.Width + u];
|
||||
}
|
||||
|
||||
set
|
||||
{
|
||||
entries[v * Size.Width + u] = value;
|
||||
}
|
||||
get { return entries[Index(u, v)]; }
|
||||
set { entries[Index(u, v)] = value; }
|
||||
}
|
||||
|
||||
/// <summary>Clears the layer contents with a known value</summary>
|
||||
|
||||
@@ -11,8 +11,7 @@
|
||||
using System;
|
||||
using System.Collections;
|
||||
using System.Collections.Generic;
|
||||
using System.Drawing;
|
||||
using OpenRA.Graphics;
|
||||
using System.Linq;
|
||||
|
||||
namespace OpenRA
|
||||
{
|
||||
@@ -50,29 +49,69 @@ namespace OpenRA
|
||||
return new CellRegion(region.shape, tl, br);
|
||||
}
|
||||
|
||||
/// <summary>Returns the minimal region that covers at least the specified cells.</summary>
|
||||
public static CellRegion BoundingRegion(TileShape shape, IEnumerable<CPos> cells)
|
||||
{
|
||||
if (cells == null || !cells.Any())
|
||||
throw new ArgumentException("cells must not be null or empty.", "cells");
|
||||
|
||||
var minX = int.MaxValue;
|
||||
var minY = int.MaxValue;
|
||||
var maxX = int.MinValue;
|
||||
var maxY = int.MinValue;
|
||||
foreach (var cell in cells)
|
||||
{
|
||||
if (minX > cell.X)
|
||||
minX = cell.X;
|
||||
if (maxX < cell.X)
|
||||
maxX = cell.X;
|
||||
if (minY > cell.Y)
|
||||
minY = cell.Y;
|
||||
if (maxY < cell.Y)
|
||||
maxY = cell.Y;
|
||||
}
|
||||
|
||||
return new CellRegion(shape, new CPos(minX, minY), new CPos(maxX, maxY));
|
||||
}
|
||||
|
||||
public bool Contains(CellRegion region)
|
||||
{
|
||||
return
|
||||
TopLeft.X <= region.TopLeft.X && TopLeft.Y <= region.TopLeft.Y &&
|
||||
BottomRight.X >= region.BottomRight.X && BottomRight.Y >= region.BottomRight.Y;
|
||||
}
|
||||
|
||||
public bool Contains(CPos cell)
|
||||
{
|
||||
var uv = Map.CellToMap(shape, cell);
|
||||
return uv.X >= mapTopLeft.X && uv.X <= mapBottomRight.X && uv.Y >= mapTopLeft.Y && uv.Y <= mapBottomRight.Y;
|
||||
}
|
||||
|
||||
public IEnumerator<CPos> GetEnumerator()
|
||||
public CellRegionEnumerator GetEnumerator()
|
||||
{
|
||||
return new CellRegionEnumerator(this);
|
||||
}
|
||||
|
||||
IEnumerator<CPos> IEnumerable<CPos>.GetEnumerator()
|
||||
{
|
||||
return GetEnumerator();
|
||||
}
|
||||
|
||||
IEnumerator IEnumerable.GetEnumerator()
|
||||
{
|
||||
return GetEnumerator();
|
||||
}
|
||||
|
||||
class CellRegionEnumerator : IEnumerator<CPos>
|
||||
public class CellRegionEnumerator : IEnumerator<CPos>
|
||||
{
|
||||
readonly CellRegion r;
|
||||
|
||||
// Current position, in map coordinates
|
||||
int u, v;
|
||||
|
||||
// Current position, in cell coordinates
|
||||
CPos current;
|
||||
|
||||
public CellRegionEnumerator(CellRegion region)
|
||||
{
|
||||
r = region;
|
||||
@@ -94,6 +133,7 @@ namespace OpenRA
|
||||
return false;
|
||||
}
|
||||
|
||||
current = Map.MapToCell(r.shape, new CPos(u, v));
|
||||
return true;
|
||||
}
|
||||
|
||||
@@ -104,7 +144,7 @@ namespace OpenRA
|
||||
v = r.mapTopLeft.Y;
|
||||
}
|
||||
|
||||
public CPos Current { get { return Map.MapToCell(r.shape, new CPos(u, v)); } }
|
||||
public CPos Current { get { return current; } }
|
||||
object IEnumerator.Current { get { return Current; } }
|
||||
public void Dispose() { }
|
||||
}
|
||||
|
||||
@@ -24,11 +24,12 @@ namespace OpenRA.Traits
|
||||
public class FrozenActor
|
||||
{
|
||||
public readonly CPos[] Footprint;
|
||||
public readonly CellRegion FootprintRegion;
|
||||
public readonly WPos CenterPosition;
|
||||
public readonly Rectangle Bounds;
|
||||
readonly Actor actor;
|
||||
|
||||
public IRenderable[] Renderables { set; private get; }
|
||||
public IRenderable[] Renderables { private get; set; }
|
||||
public Player Owner;
|
||||
|
||||
public string TooltipName;
|
||||
@@ -39,10 +40,12 @@ namespace OpenRA.Traits
|
||||
|
||||
public bool Visible;
|
||||
|
||||
public FrozenActor(Actor self, IEnumerable<CPos> footprint)
|
||||
public FrozenActor(Actor self, CPos[] footprint, CellRegion footprintRegion)
|
||||
{
|
||||
actor = self;
|
||||
Footprint = footprint.ToArray();
|
||||
Footprint = footprint;
|
||||
FootprintRegion = footprintRegion;
|
||||
|
||||
CenterPosition = self.CenterPosition;
|
||||
Bounds = self.Bounds.Value;
|
||||
}
|
||||
@@ -55,16 +58,7 @@ namespace OpenRA.Traits
|
||||
int flashTicks;
|
||||
public void Tick(World world, Shroud shroud)
|
||||
{
|
||||
Visible = true;
|
||||
foreach (var pos in Footprint)
|
||||
{
|
||||
if (shroud.IsVisible(pos))
|
||||
{
|
||||
Visible = false;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
Visible = !Footprint.Any(shroud.IsVisibleTest(FootprintRegion));
|
||||
if (flashTicks > 0)
|
||||
flashTicks--;
|
||||
}
|
||||
@@ -85,6 +79,7 @@ namespace OpenRA.Traits
|
||||
return Renderables.Concat(Renderables.Where(r => !r.IsDecoration)
|
||||
.Select(r => r.WithPalette(highlight)));
|
||||
}
|
||||
|
||||
return Renderables;
|
||||
}
|
||||
|
||||
|
||||
@@ -10,7 +10,6 @@
|
||||
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Drawing;
|
||||
using System.Linq;
|
||||
|
||||
namespace OpenRA.Traits
|
||||
@@ -23,31 +22,37 @@ namespace OpenRA.Traits
|
||||
|
||||
public class Shroud
|
||||
{
|
||||
[Sync] public bool Disabled = false;
|
||||
[Sync] public bool Disabled;
|
||||
|
||||
Actor self;
|
||||
Map map;
|
||||
readonly Actor self;
|
||||
readonly Map map;
|
||||
|
||||
CellLayer<int> visibleCount;
|
||||
CellLayer<int> generatedShroudCount;
|
||||
CellLayer<bool> explored;
|
||||
readonly CellLayer<short> visibleCount;
|
||||
readonly CellLayer<short> generatedShroudCount;
|
||||
readonly CellLayer<bool> explored;
|
||||
|
||||
readonly Lazy<IFogVisibilityModifier[]> fogVisibilities;
|
||||
|
||||
// Cache of visibility that was added, so no matter what crazy trait code does, it
|
||||
// can't make us invalid.
|
||||
Dictionary<Actor, CPos[]> visibility = new Dictionary<Actor, CPos[]>();
|
||||
Dictionary<Actor, CPos[]> generation = new Dictionary<Actor, CPos[]>();
|
||||
readonly Dictionary<Actor, CPos[]> visibility = new Dictionary<Actor, CPos[]>();
|
||||
readonly Dictionary<Actor, CPos[]> generation = new Dictionary<Actor, CPos[]>();
|
||||
|
||||
public int Hash { get; private set; }
|
||||
|
||||
static readonly Func<CPos, bool> TruthPredicate = cell => true;
|
||||
readonly Func<CPos, bool> fastExploredTest;
|
||||
readonly Func<CPos, bool> slowExploredTest;
|
||||
readonly Func<CPos, bool> fastVisibleTest;
|
||||
readonly Func<CPos, bool> slowVisibleTest;
|
||||
|
||||
public Shroud(Actor self)
|
||||
{
|
||||
this.self = self;
|
||||
map = self.World.Map;
|
||||
|
||||
visibleCount = new CellLayer<int>(map);
|
||||
generatedShroudCount = new CellLayer<int>(map);
|
||||
visibleCount = new CellLayer<short>(map);
|
||||
generatedShroudCount = new CellLayer<short>(map);
|
||||
explored = new CellLayer<bool>(map);
|
||||
|
||||
self.World.ActorAdded += AddVisibility;
|
||||
@@ -57,6 +62,11 @@ namespace OpenRA.Traits
|
||||
self.World.ActorRemoved += RemoveShroudGeneration;
|
||||
|
||||
fogVisibilities = Exts.Lazy(() => self.TraitsImplementing<IFogVisibilityModifier>().ToArray());
|
||||
|
||||
fastExploredTest = IsExploredCore;
|
||||
slowExploredTest = IsExplored;
|
||||
fastVisibleTest = IsVisibleCore;
|
||||
slowVisibleTest = IsVisible;
|
||||
}
|
||||
|
||||
void Invalidate()
|
||||
@@ -187,14 +197,17 @@ namespace OpenRA.Traits
|
||||
|
||||
public void Explore(World world, CPos center, WRange range)
|
||||
{
|
||||
foreach (var q in FindVisibleTiles(world, center, range))
|
||||
explored[q] = true;
|
||||
foreach (var c in FindVisibleTiles(world, center, range))
|
||||
explored[c] = true;
|
||||
|
||||
Invalidate();
|
||||
}
|
||||
|
||||
public void Explore(Shroud s)
|
||||
{
|
||||
if (map.Bounds != s.map.Bounds)
|
||||
throw new ArgumentException("The map bounds of these shrouds do not match.", "s");
|
||||
|
||||
foreach (var cell in map.Cells)
|
||||
if (s.explored[cell])
|
||||
explored[cell] = true;
|
||||
@@ -222,10 +235,32 @@ namespace OpenRA.Traits
|
||||
if (!map.Contains(cell))
|
||||
return false;
|
||||
|
||||
if (Disabled || !self.World.LobbyInfo.GlobalSettings.Shroud)
|
||||
if (!ShroudEnabled)
|
||||
return true;
|
||||
|
||||
return explored[cell] && (generatedShroudCount[cell] == 0 || visibleCount[cell] > 0);
|
||||
return IsExploredCore(cell);
|
||||
}
|
||||
|
||||
bool ShroudEnabled { get { return !Disabled && self.World.LobbyInfo.GlobalSettings.Shroud; } }
|
||||
|
||||
bool IsExploredCore(CPos cell)
|
||||
{
|
||||
var uv = Map.CellToMap(map.TileShape, cell);
|
||||
return explored[uv.X, uv.Y] && (generatedShroudCount[uv.X, uv.Y] == 0 || visibleCount[uv.X, uv.Y] > 0);
|
||||
}
|
||||
|
||||
public Func<CPos, bool> IsExploredTest(CellRegion region)
|
||||
{
|
||||
// If the region to test extends outside the map we must use the slow test that checks the map boundary every time.
|
||||
if (!map.Cells.Contains(region))
|
||||
return slowExploredTest;
|
||||
|
||||
// If shroud isn't enabled, then we can see everything.
|
||||
if (!ShroudEnabled)
|
||||
return TruthPredicate;
|
||||
|
||||
// If shroud is enabled, we can use the fast test that just does the core check.
|
||||
return fastExploredTest;
|
||||
}
|
||||
|
||||
public bool IsExplored(Actor a)
|
||||
@@ -238,12 +273,33 @@ namespace OpenRA.Traits
|
||||
if (!map.Contains(cell))
|
||||
return false;
|
||||
|
||||
if (Disabled || !self.World.LobbyInfo.GlobalSettings.Fog)
|
||||
if (!FogEnabled)
|
||||
return true;
|
||||
|
||||
return IsVisibleCore(cell);
|
||||
}
|
||||
|
||||
bool FogEnabled { get { return !Disabled && self.World.LobbyInfo.GlobalSettings.Fog; } }
|
||||
|
||||
bool IsVisibleCore(CPos cell)
|
||||
{
|
||||
return visibleCount[cell] > 0;
|
||||
}
|
||||
|
||||
public Func<CPos, bool> IsVisibleTest(CellRegion region)
|
||||
{
|
||||
// If the region to test extends outside the map we must use the slow test that checks the map boundary every time.
|
||||
if (!map.Cells.Contains(region))
|
||||
return slowVisibleTest;
|
||||
|
||||
// If fog isn't enabled, then we can see everything.
|
||||
if (!FogEnabled)
|
||||
return TruthPredicate;
|
||||
|
||||
// If fog is enabled, we can use the fast test that just does the core check.
|
||||
return fastVisibleTest;
|
||||
}
|
||||
|
||||
// Actors are hidden under shroud, but not under fog by default
|
||||
public bool IsVisible(Actor a)
|
||||
{
|
||||
|
||||
@@ -10,7 +10,6 @@
|
||||
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Drawing;
|
||||
using System.Linq;
|
||||
using OpenRA.Effects;
|
||||
using OpenRA.FileFormats;
|
||||
@@ -25,6 +24,7 @@ namespace OpenRA
|
||||
{
|
||||
public class World
|
||||
{
|
||||
static readonly Func<CPos, bool> FalsePredicate = cell => false;
|
||||
internal readonly TraitDictionary traitDict = new TraitDictionary();
|
||||
readonly HashSet<Actor> actors = new HashSet<Actor>();
|
||||
readonly List<IEffect> effects = new List<IEffect>();
|
||||
@@ -54,6 +54,24 @@ namespace OpenRA
|
||||
public bool FogObscures(CPos p) { return RenderPlayer != null && !RenderPlayer.Shroud.IsVisible(p); }
|
||||
public bool ShroudObscures(Actor a) { return RenderPlayer != null && !RenderPlayer.Shroud.IsExplored(a); }
|
||||
public bool ShroudObscures(CPos p) { return RenderPlayer != null && !RenderPlayer.Shroud.IsExplored(p); }
|
||||
|
||||
public Func<CPos, bool> FogObscuresTest(CellRegion region)
|
||||
{
|
||||
var rp = RenderPlayer;
|
||||
if (rp == null)
|
||||
return FalsePredicate;
|
||||
var predicate = rp.Shroud.IsVisibleTest(region);
|
||||
return cell => !predicate(cell);
|
||||
}
|
||||
|
||||
public Func<CPos, bool> ShroudObscuresTest(CellRegion region)
|
||||
{
|
||||
var rp = RenderPlayer;
|
||||
if (rp == null)
|
||||
return FalsePredicate;
|
||||
var predicate = rp.Shroud.IsExploredTest(region);
|
||||
return cell => !predicate(cell);
|
||||
}
|
||||
|
||||
public bool IsReplay
|
||||
{
|
||||
|
||||
Reference in New Issue
Block a user