Tweaks for map generator performance.
The map generator is already performant, but we can remove a few rough edges from infrastructure it interacts with. - Add TypeDictionary constructor to clone from an existing copy. The map editor infrastructure relies heavily on cloning ActorReferences and the underlying dictionaries. A dedicated clone method avoids reflection overhead for CreateTypeContainer on a fresh instance. - Use ThrowIndexOutOfRangeException helper. This allows the Index method to be inlined. - Reuse lists in FloodFill. This avoids allocating and resizing new lists on each loop when we can reuse the existing capacity. - Manage CellEntryChanged at the layer rather than individual previews. We only need to attach/detach from the delegate once at the layer, rather than many times as previews are added and removed.
This commit is contained in:
committed by
Gustas Kažukauskas
parent
3ea5b08848
commit
a546ca2f92
@@ -54,13 +54,8 @@ namespace OpenRA
|
||||
public ActorReference(string type, TypeDictionary inits)
|
||||
{
|
||||
Type = type;
|
||||
initDict = new Lazy<TypeDictionary>(() =>
|
||||
{
|
||||
var dict = new TypeDictionary();
|
||||
foreach (var i in inits)
|
||||
dict.Add(i);
|
||||
return dict;
|
||||
});
|
||||
var initsClone = new TypeDictionary(inits);
|
||||
initDict = Exts.Lazy(() => initsClone);
|
||||
}
|
||||
|
||||
static ActorInit LoadInit(string initName, MiniYaml initYaml)
|
||||
@@ -110,11 +105,7 @@ namespace OpenRA
|
||||
|
||||
public ActorReference Clone()
|
||||
{
|
||||
var clone = new ActorReference(Type);
|
||||
foreach (var init in initDict.Value)
|
||||
clone.initDict.Value.Add(init);
|
||||
|
||||
return clone;
|
||||
return new ActorReference(Type, initDict.Value);
|
||||
}
|
||||
|
||||
public void Add(ActorInit init)
|
||||
|
||||
@@ -12,6 +12,7 @@
|
||||
using System;
|
||||
using System.Collections;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
|
||||
namespace OpenRA.Primitives
|
||||
{
|
||||
@@ -20,7 +21,17 @@ namespace OpenRA.Primitives
|
||||
static readonly Func<Type, ITypeContainer> CreateTypeContainer = t =>
|
||||
(ITypeContainer)typeof(TypeContainer<>).MakeGenericType(t).GetConstructor(Type.EmptyTypes).Invoke(null);
|
||||
|
||||
readonly Dictionary<Type, ITypeContainer> data = [];
|
||||
readonly Dictionary<Type, ITypeContainer> data;
|
||||
|
||||
public TypeDictionary()
|
||||
{
|
||||
data = [];
|
||||
}
|
||||
|
||||
public TypeDictionary(TypeDictionary cloneFrom)
|
||||
{
|
||||
data = cloneFrom.data.ToDictionary(kvp => kvp.Key, kvp => kvp.Value.Clone());
|
||||
}
|
||||
|
||||
ITypeContainer InnerGet(Type t)
|
||||
{
|
||||
@@ -127,11 +138,12 @@ namespace OpenRA.Primitives
|
||||
void Add(object value);
|
||||
void Remove(object value);
|
||||
void TrimExcess();
|
||||
ITypeContainer Clone();
|
||||
}
|
||||
|
||||
sealed class TypeContainer<T> : ITypeContainer
|
||||
{
|
||||
public List<T> Objects { get; } = [];
|
||||
public List<T> Objects { get; private init; } = [];
|
||||
|
||||
public int Count => Objects.Count;
|
||||
|
||||
@@ -149,6 +161,11 @@ namespace OpenRA.Primitives
|
||||
{
|
||||
Objects.TrimExcess();
|
||||
}
|
||||
|
||||
public ITypeContainer Clone()
|
||||
{
|
||||
return new TypeContainer<T>() { Objects = Objects.ToList() };
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -561,11 +561,12 @@ namespace OpenRA.Mods.Common.MapGenerator
|
||||
Func<CPos, P, P?> filler,
|
||||
ImmutableArray<CVec> spread) where P : struct
|
||||
{
|
||||
var current = new List<(CPos CPos, P Prop)>();
|
||||
var next = seeds.ToList();
|
||||
while (next.Count != 0)
|
||||
{
|
||||
var current = next;
|
||||
next = [];
|
||||
(next, current) = (current, next);
|
||||
next.Clear();
|
||||
foreach (var (source, prop) in current)
|
||||
{
|
||||
var newProp = filler(source, prop);
|
||||
|
||||
@@ -57,11 +57,16 @@ namespace OpenRA.Mods.Common.MapGenerator
|
||||
public int Index(int x, int y)
|
||||
{
|
||||
if (!ContainsXY(x, y))
|
||||
throw new IndexOutOfRangeException(
|
||||
$"({x}, {y}) is out of bounds for a matrix of size ({Size.X}, {Size.Y})");
|
||||
ThrowIndexOutOfRangeException(x, y);
|
||||
return y * Size.X + x;
|
||||
}
|
||||
|
||||
void ThrowIndexOutOfRangeException(int x, int y)
|
||||
{
|
||||
throw new IndexOutOfRangeException(
|
||||
$"({x}, {y}) is out of bounds for a matrix of size ({Size.X}, {Size.Y})");
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Convert a Data index into a pair of coordinates.
|
||||
/// </summary>
|
||||
|
||||
@@ -345,11 +345,12 @@ namespace OpenRA.Mods.Common.MapGenerator
|
||||
Func<int2, P, P?> filler,
|
||||
ImmutableArray<int2> spread) where P : struct
|
||||
{
|
||||
var current = new List<(int2 XY, P Prop)>();
|
||||
var next = seeds.ToList();
|
||||
while (next.Count != 0)
|
||||
{
|
||||
var current = next;
|
||||
next = [];
|
||||
(next, current) = (current, next);
|
||||
next.Clear();
|
||||
foreach (var (source, prop) in current)
|
||||
{
|
||||
var newProp = filler(source, prop);
|
||||
|
||||
@@ -54,9 +54,7 @@ namespace OpenRA.Mods.Common.Traits
|
||||
var exitLocations = new List<CPos>();
|
||||
|
||||
// Clone the initializer dictionary for the new actor
|
||||
var td = new TypeDictionary();
|
||||
foreach (var init in inits)
|
||||
td.Add(init);
|
||||
var td = new TypeDictionary(inits);
|
||||
|
||||
if (exitinfo != null && self.OccupiesSpace != null && producee.HasTraitInfo<IOccupySpaceInfo>())
|
||||
{
|
||||
|
||||
@@ -82,13 +82,12 @@ namespace OpenRA.Mods.Common.Traits
|
||||
|
||||
self.World.AddFrameEndTask(w =>
|
||||
{
|
||||
var td = new TypeDictionary();
|
||||
foreach (var init in inits)
|
||||
td.Add(init);
|
||||
|
||||
td.Add(new LocationInit(location.Value));
|
||||
td.Add(new CenterPositionInit(pos));
|
||||
td.Add(new FacingInit(initialFacing));
|
||||
var td = new TypeDictionary(inits)
|
||||
{
|
||||
new LocationInit(location.Value),
|
||||
new CenterPositionInit(pos),
|
||||
new FacingInit(initialFacing)
|
||||
};
|
||||
|
||||
var newUnit = self.World.CreateActor(producee.Name, td);
|
||||
|
||||
|
||||
@@ -122,9 +122,7 @@ namespace OpenRA.Mods.Common.Traits
|
||||
var altitude = self.World.Map.Rules.Actors[actorType].TraitInfo<AircraftInfo>().CruiseAltitude;
|
||||
|
||||
// Clone the initializer dictionary for the new actor
|
||||
var td = new TypeDictionary();
|
||||
foreach (var init in inits)
|
||||
td.Add(init);
|
||||
var td = new TypeDictionary(inits);
|
||||
|
||||
if (self.OccupiesSpace != null)
|
||||
{
|
||||
|
||||
@@ -42,7 +42,7 @@ namespace OpenRA.Mods.Common.Traits
|
||||
public override object Create(ActorInitializer init) { return new EditorActorLayer(this); }
|
||||
}
|
||||
|
||||
public class EditorActorLayer : IWorldLoaded, ITickRender, IRender, IRadarSignature, ICreatePlayers, IRenderAnnotations
|
||||
public class EditorActorLayer : IWorldLoaded, ITickRender, IRender, IRadarSignature, ICreatePlayers, IRenderAnnotations, INotifyActorDisposing
|
||||
{
|
||||
const string ActorPrefix = "Actor";
|
||||
const string PlayerSpawnName = "mpspawn";
|
||||
@@ -101,6 +101,9 @@ namespace OpenRA.Mods.Common.Traits
|
||||
}
|
||||
|
||||
AddRange(CollectionsMarshal.AsSpan(references), names);
|
||||
|
||||
world.Map.Height.CellEntryChanged += UpdatePreviewsOnMapChange;
|
||||
world.Map.Ramp.CellEntryChanged += UpdatePreviewsOnMapChange;
|
||||
}
|
||||
|
||||
void ITickRender.TickRender(WorldRenderer wr, Actor self)
|
||||
@@ -400,7 +403,7 @@ namespace OpenRA.Mods.Common.Traits
|
||||
public IEnumerable<EditorActorPreview> PreviewsAtCell(CPos cell)
|
||||
{
|
||||
return cellMap.At(new int2(cell.X - cellOffset.X, cell.Y - cellOffset.Y))
|
||||
.Where(p => OccupiedCells(p).Any(fp => fp == cell));
|
||||
.Where(p => OccupiedCells(p).Contains(cell));
|
||||
}
|
||||
|
||||
public SubCell FreeSubCellAt(CPos cell)
|
||||
@@ -426,6 +429,12 @@ namespace OpenRA.Mods.Common.Traits
|
||||
return screenMap.At(worldPx);
|
||||
}
|
||||
|
||||
void UpdatePreviewsOnMapChange(CPos changedCell)
|
||||
{
|
||||
foreach (var preview in PreviewsAtCell(changedCell))
|
||||
preview.UpdateFromCellChange();
|
||||
}
|
||||
|
||||
public Action OnPlayerRemoved = () => { };
|
||||
|
||||
static bool TryGetActorId(string name, out uint id)
|
||||
@@ -472,6 +481,12 @@ namespace OpenRA.Mods.Common.Traits
|
||||
destinationBuffer.Add((cell, preview.RadarColor));
|
||||
}
|
||||
|
||||
void INotifyActorDisposing.Disposing(Actor self)
|
||||
{
|
||||
self.World.Map.Height.CellEntryChanged -= UpdatePreviewsOnMapChange;
|
||||
self.World.Map.Ramp.CellEntryChanged -= UpdatePreviewsOnMapChange;
|
||||
}
|
||||
|
||||
public EditorActorPreview this[string id]
|
||||
{
|
||||
get { return previews.FirstOrDefault(p => p.ID.Equals(id, StringComparison.OrdinalIgnoreCase)); }
|
||||
|
||||
@@ -46,7 +46,6 @@ namespace OpenRA.Mods.Common.Traits
|
||||
readonly TooltipInfoBase tooltip;
|
||||
readonly ActorReference reference;
|
||||
readonly Dictionary<INotifyEditorPlacementInfo, object> editorData = [];
|
||||
readonly Action<CPos> onCellEntryChanged;
|
||||
|
||||
SelectionBoxAnnotationRenderable selectionBox;
|
||||
IActorPreview[] previews;
|
||||
@@ -69,7 +68,7 @@ namespace OpenRA.Mods.Common.Traits
|
||||
throw new InvalidDataException($"Actor {id} of unknown type {reference.Type.ToLowerInvariant()}");
|
||||
|
||||
GenerateFootprint();
|
||||
UpdateFromCellChange(null);
|
||||
UpdateFromCellChange();
|
||||
|
||||
tooltip = Info.TraitInfos<EditorOnlyTooltipInfo>().FirstOrDefault(info => info.EnabledByDefault) as TooltipInfoBase
|
||||
?? Info.TraitInfos<TooltipInfo>().FirstOrDefault(info => info.EnabledByDefault);
|
||||
@@ -78,8 +77,6 @@ namespace OpenRA.Mods.Common.Traits
|
||||
|
||||
terrainRadarColorInfo = Info.TraitInfoOrDefault<RadarColorFromTerrainInfo>();
|
||||
UpdateRadarColor();
|
||||
|
||||
onCellEntryChanged = cell => UpdateFromCellChange(cell);
|
||||
}
|
||||
|
||||
public EditorActorPreview WithId(string id)
|
||||
@@ -87,11 +84,8 @@ namespace OpenRA.Mods.Common.Traits
|
||||
return new EditorActorPreview(worldRenderer, id, reference.Clone(), Owner);
|
||||
}
|
||||
|
||||
void UpdateFromCellChange(CPos? cellChanged)
|
||||
public void UpdateFromCellChange()
|
||||
{
|
||||
if (cellChanged != null && !Footprint.ContainsKey(cellChanged.Value))
|
||||
return;
|
||||
|
||||
CenterPosition = PreviewPosition(worldRenderer.World, reference);
|
||||
GeneratePreviews();
|
||||
GenerateBounds();
|
||||
@@ -183,18 +177,12 @@ namespace OpenRA.Mods.Common.Traits
|
||||
{
|
||||
foreach (var notify in Info.TraitInfos<INotifyEditorPlacementInfo>())
|
||||
editorData[notify] = notify.AddedToEditor(this, worldRenderer.World);
|
||||
|
||||
worldRenderer.World.Map.Height.CellEntryChanged += onCellEntryChanged;
|
||||
worldRenderer.World.Map.Ramp.CellEntryChanged += onCellEntryChanged;
|
||||
}
|
||||
|
||||
public void RemovedFromEditor()
|
||||
{
|
||||
foreach (var kv in editorData)
|
||||
kv.Key.RemovedFromEditor(this, worldRenderer.World, kv.Value);
|
||||
|
||||
worldRenderer.World.Map.Height.CellEntryChanged -= onCellEntryChanged;
|
||||
worldRenderer.World.Map.Ramp.CellEntryChanged -= onCellEntryChanged;
|
||||
}
|
||||
|
||||
public void AddInit<T>(T init) where T : ActorInit
|
||||
|
||||
Reference in New Issue
Block a user