Merge pull request #7057 from pchote/celllayerupdates
Improve radar rendering performance
This commit is contained in:
@@ -20,6 +20,8 @@ namespace OpenRA
|
||||
{
|
||||
public readonly Size Size;
|
||||
public readonly TileShape Shape;
|
||||
public event Action<CPos> CellEntryChanged = null;
|
||||
|
||||
readonly T[] entries;
|
||||
|
||||
public CellLayer(Map map)
|
||||
@@ -48,15 +50,35 @@ namespace OpenRA
|
||||
/// <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>
|
||||
|
||||
@@ -31,6 +31,23 @@ namespace OpenRA.Traits
|
||||
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
|
||||
|
||||
@@ -12,6 +12,7 @@ using System;
|
||||
using System.Drawing;
|
||||
using System.Linq;
|
||||
using OpenRA.Graphics;
|
||||
using OpenRA.Traits;
|
||||
using OpenRA.Widgets;
|
||||
|
||||
namespace OpenRA.Mods.Common.Widgets
|
||||
@@ -31,16 +32,18 @@ namespace OpenRA.Mods.Common.Widgets
|
||||
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;
|
||||
@@ -68,19 +71,62 @@ namespace OpenRA.Mods.Common.Widgets
|
||||
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))
|
||||
{
|
||||
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);
|
||||
// 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();
|
||||
|
||||
// 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);
|
||||
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
|
||||
{
|
||||
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;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -153,9 +199,10 @@ namespace OpenRA.Mods.Common.Widgets
|
||||
|
||||
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)
|
||||
@@ -195,31 +242,67 @@ namespace OpenRA.Mods.Common.Widgets
|
||||
|
||||
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)
|
||||
|
||||
Reference in New Issue
Block a user