Skip to content

Commit

Permalink
Merge pull request OpenRA#7057 from pchote/celllayerupdates
Browse files Browse the repository at this point in the history
Improve radar rendering performance
  • Loading branch information
chrisforbes committed Dec 8, 2014
2 parents 522c63f + af9a74d commit eec4f71
Show file tree
Hide file tree
Showing 3 changed files with 161 additions and 39 deletions.
30 changes: 26 additions & 4 deletions OpenRA.Game/Map/CellLayer.cs
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,8 @@ public class CellLayer<T> : IEnumerable<T>
{
public readonly Size Size;
public readonly TileShape Shape;
public event Action<CPos> CellEntryChanged = null;

readonly T[] entries;

public CellLayer(Map map)
Expand Down Expand Up @@ -48,15 +50,35 @@ int Index(int u, int v)
/// <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;

if (CellEntryChanged != null)
CellEntryChanged(cell);
}
}

/// <summary>Gets or sets the layer contents using raw map coordinates (not CPos!)</summary>
public T this[int u, int v]
{
get { return entries[Index(u, v)]; }
set { entries[Index(u, v)] = value; }
get
{
return entries[Index(u, v)];
}

set
{
entries[Index(u, v)] = value;

if (CellEntryChanged != null)
CellEntryChanged(Map.MapToCell(Shape, new CPos(u, v)));
}
}

/// <summary>Clears the layer contents with a known value</summary>
Expand Down
17 changes: 17 additions & 0 deletions OpenRA.Game/Traits/World/Shroud.cs
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,23 @@ public class Shroud
readonly CellLayer<short> generatedShroudCount;
readonly CellLayer<bool> explored;

public event Action<CPos> CellEntryChanged
{
add
{
visibleCount.CellEntryChanged += value;
generatedShroudCount.CellEntryChanged += value;
explored.CellEntryChanged += value;
}

remove
{
visibleCount.CellEntryChanged -= value;
generatedShroudCount.CellEntryChanged -= value;
explored.CellEntryChanged -= value;
}
}

readonly Lazy<IFogVisibilityModifier[]> fogVisibilities;

// Cache of visibility that was added, so no matter what crazy trait code does, it
Expand Down
153 changes: 118 additions & 35 deletions OpenRA.Mods.Common/Widgets/RadarWidget.cs
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@
using System.Drawing;
using System.Linq;
using OpenRA.Graphics;
using OpenRA.Traits;
using OpenRA.Widgets;

namespace OpenRA.Mods.Common.Widgets
Expand All @@ -31,16 +32,18 @@ public class RadarWidget : Widget
int frame;
bool hasRadar;
bool cachedEnabled;
int updateTicks;

float previewScale = 0;
int2 previewOrigin = int2.Zero;
Rectangle mapRect = Rectangle.Empty;

Sheet radarSheet;
byte[] radarData;

Sprite terrainSprite;
Sprite customTerrainSprite;
Sprite actorSprite;
Sprite shroudSprite;
Shroud renderShroud;

readonly World world;
readonly WorldRenderer worldRenderer;
Expand Down Expand Up @@ -68,19 +71,62 @@ public override void Initialize(WidgetArgs args)
previewOrigin = new int2((int)(previewScale * (size - width) / 2), (int)(previewScale * (size - height) / 2));
mapRect = new Rectangle(previewOrigin.X, previewOrigin.Y, (int)(previewScale * width), (int)(previewScale * height));

// Only needs to be done once
using (var terrainBitmap = Minimap.TerrainBitmap(world.Map.Rules.TileSets[world.Map.Tileset], world.Map))
// The four layers are stored in a 2x2 grid within a single texture
radarSheet = new Sheet(new Size(2 * width, 2 * height).NextPowerOf2());
radarSheet.CreateBuffer();
radarData = radarSheet.GetData();

terrainSprite = new Sprite(radarSheet, new Rectangle(0, 0, width, height), TextureChannel.Alpha);
shroudSprite = new Sprite(radarSheet, new Rectangle(width, 0, width, height), TextureChannel.Alpha);
actorSprite = new Sprite(radarSheet, new Rectangle(0, height, width, height), TextureChannel.Alpha);

// Set initial terrain data
using (var bitmap = Minimap.TerrainBitmap(world.TileSet, world.Map))
OpenRA.Graphics.Util.FastCopyIntoSprite(terrainSprite, bitmap);

world.Map.MapTiles.Value.CellEntryChanged += UpdateTerrainCell;
world.Map.CustomTerrain.CellEntryChanged += UpdateTerrainCell;
}

void UpdateTerrainCell(CPos cell)
{
var stride = radarSheet.Size.Width;
var uv = Map.CellToMap(world.Map.TileShape, cell);
var terrain = world.Map.GetTerrainInfo(cell);

var dx = terrainSprite.bounds.Left - world.Map.Bounds.Left;
var dy = terrainSprite.bounds.Top - world.Map.Bounds.Top;

unsafe
{
var r = new Rectangle(0, 0, width, height);
var s = new Size(terrainBitmap.Width, terrainBitmap.Height);
var terrainSheet = new Sheet(s);
terrainSheet.GetTexture().SetData(terrainBitmap);
terrainSprite = new Sprite(terrainSheet, r, TextureChannel.Alpha);

// Data is set in Tick()
customTerrainSprite = new Sprite(new Sheet(s), r, TextureChannel.Alpha);
actorSprite = new Sprite(new Sheet(s), r, TextureChannel.Alpha);
shroudSprite = new Sprite(new Sheet(s), r, TextureChannel.Alpha);
fixed (byte* _colors = &radarData[0])
{
var colors = (int*)_colors;
colors[(uv.Y + dy) * stride + uv.X + dx] = terrain.Color.ToArgb();
}
}
}

void UpdateShroudCell(CPos cell)
{
var stride = radarSheet.Size.Width;
var uv = Map.CellToMap(world.Map.TileShape, cell);
var dx = shroudSprite.bounds.Left - world.Map.Bounds.Left;
var dy = shroudSprite.bounds.Top - world.Map.Bounds.Top;

var color = 0;
if (world.ShroudObscures(cell))
color = Color.Black.ToArgb();
else if (world.FogObscures(cell))
color = Color.FromArgb(128, Color.Black).ToArgb();

unsafe
{
fixed (byte* _colors = &radarData[0])
{
var colors = (int*)_colors;
colors[(uv.Y + dy) * stride + uv.X + dx] = color;
}
}
}

Expand Down Expand Up @@ -153,9 +199,10 @@ public override void Draw()

var rsr = Game.Renderer.RgbaSpriteRenderer;
rsr.DrawSprite(terrainSprite, o, s);
rsr.DrawSprite(customTerrainSprite, o, s);
rsr.DrawSprite(actorSprite, o, s);
rsr.DrawSprite(shroudSprite, o, s);

if (renderShroud != null)
rsr.DrawSprite(shroudSprite, o, s);

// Draw viewport rect
if (hasRadar)
Expand Down Expand Up @@ -195,31 +242,67 @@ void DrawRadarPings()

public override void Tick()
{
// Update the radar animation even when its closed
// This avoids obviously stale data from being shown when first opened.
// TODO: This delayed updating is a giant hack
--updateTicks;
if (updateTicks <= 0)
{
updateTicks = 12;
using (var bitmap = Minimap.CustomTerrainBitmap(world))
customTerrainSprite.sheet.GetTexture().SetData(bitmap);
}

if (updateTicks == 8)
using (var bitmap = Minimap.ActorsBitmap(world))
actorSprite.sheet.GetTexture().SetData(bitmap);

if (updateTicks == 4)
using (var bitmap = Minimap.ShroudBitmap(world))
shroudSprite.sheet.GetTexture().SetData(bitmap);

// Enable/Disable the radar
var enabled = IsEnabled();
if (enabled != cachedEnabled)
Sound.Play(enabled ? RadarOnlineSound : RadarOfflineSound);
cachedEnabled = enabled;

if (enabled)
{
var rp = world.RenderPlayer;
var newRenderShroud = rp != null ? rp.Shroud : null;
if (newRenderShroud != renderShroud)
{
if (renderShroud != null)
renderShroud.CellEntryChanged -= UpdateShroudCell;

if (newRenderShroud != null)
{
// Redraw the full shroud sprite
using (var bitmap = Minimap.ShroudBitmap(world))
OpenRA.Graphics.Util.FastCopyIntoSprite(shroudSprite, bitmap);

// Update the notification binding
newRenderShroud.CellEntryChanged += UpdateShroudCell;
}

renderShroud = newRenderShroud;
}

// The actor layer is updated every tick
var stride = radarSheet.Size.Width;
var dx = actorSprite.bounds.Left - world.Map.Bounds.Left;
var dy = actorSprite.bounds.Top - world.Map.Bounds.Top;

Array.Clear(radarData, 4 * (actorSprite.bounds.Top * stride + actorSprite.bounds.Left), 4 * actorSprite.bounds.Height * stride);

unsafe
{
fixed (byte* _colors = &radarData[0])
{
var colors = (int*)_colors;

foreach (var t in world.ActorsWithTrait<IRadarSignature>())
{
if (!t.Actor.IsInWorld || world.FogObscures(t.Actor))
continue;

var color = t.Trait.RadarSignatureColor(t.Actor);
foreach (var cell in t.Trait.RadarSignatureCells(t.Actor))
{
var uv = Map.CellToMap(world.Map.TileShape, cell);

if (world.Map.Bounds.Contains(uv.X, uv.Y))
colors[(uv.Y + dy) * stride + uv.X + dx] = color.ToArgb();
}
}
}
}

radarSheet.CommitData();
}

var targetFrame = enabled ? AnimationLength : 0;
hasRadar = enabled && frame == AnimationLength;
if (frame == targetFrame)
Expand Down

0 comments on commit eec4f71

Please sign in to comment.