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:
RoosterDragon
2025-11-24 19:10:39 +00:00
committed by Gustas Kažukauskas
parent 3ea5b08848
commit a546ca2f92
10 changed files with 62 additions and 49 deletions

View File

@@ -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)); }

View File

@@ -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