Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

ActorMap, optmizations. #20351

Closed
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
5 changes: 5 additions & 0 deletions OpenRA.Game/Actor.cs
Expand Up @@ -35,6 +35,8 @@ public enum SystemActors

public sealed class Actor : IScriptBindable, IScriptNotifyBind, ILuaTableBinding, ILuaEqualityBinding, ILuaToStringBinding, IEquatable<Actor>, IDisposable
{
public interface IActorMapPositionKey { }

internal readonly struct SyncHash
{
public readonly ISync Trait;
Expand All @@ -55,6 +57,9 @@ public sealed class Actor : IScriptBindable, IScriptNotifyBind, ILuaTableBinding
public bool WillDispose { get; private set; }
public bool Disposed { get; private set; }

/// <summary>Optional key maintained by the actor map.</summary>
public IActorMapPositionKey ActorMapPositionKey { get; set; }

Activity currentActivity;
public Activity CurrentActivity
{
Expand Down
103 changes: 57 additions & 46 deletions OpenRA.Mods.Common/Traits/World/ActorMap.cs
Expand Up @@ -182,8 +182,7 @@
// Position updates are done in one pass
// to ensure consistency during a tick
readonly HashSet<Actor> addActorPosition = new();
readonly HashSet<Actor> removeActorPosition = new();
readonly Predicate<Actor> actorShouldBeRemoved;
readonly List<(Bin, Actor)> removeActorPosition = new();

public WDist LargestActorRadius { get; }
public WDist LargestBlockingActorRadius { get; }
Expand All @@ -201,9 +200,6 @@
for (var col = 0; col < cols; col++)
bins[row * cols + col] = new Bin();

// PERF: Cache this delegate so it does not have to be allocated repeatedly.
actorShouldBeRemoved = removeActorPosition.Contains;

LargestActorRadius = map.Rules.Actors.SelectMany(a => a.Value.TraitInfos<HitShapeInfo>()).Max(h => h.Type.OuterRadius);
var blockers = map.Rules.Actors.Where(a => a.Value.HasTraitInfo<IBlocksProjectilesInfo>());
LargestBlockingActorRadius = blockers.Any() ? blockers.SelectMany(a => a.Value.TraitInfos<HitShapeInfo>()).Max(h => h.Type.OuterRadius) : WDist.Zero;
Expand Down Expand Up @@ -247,7 +243,8 @@
{
Current = node.Actor;
node = node.Next;
return true;
if (Current.IsInWorld)
return true;
}

return false;
Expand Down Expand Up @@ -298,14 +295,15 @@
if (!layer.Contains(uv))
return preferredSubCell != SubCell.Any ? preferredSubCell : SubCell.First;

if (preferredSubCell != SubCell.Any && !AnyActorsAt(uv, cell, layer, preferredSubCell, checkTransient))
var influenceNode = layer[uv];
if (preferredSubCell != SubCell.Any && !AnyActorsAt(influenceNode, cell, preferredSubCell, checkTransient))
return preferredSubCell;

if (!AnyActorsAt(uv, layer))
if (influenceNode == null)
return map.Grid.DefaultSubCell;

for (var i = (int)SubCell.First; i < map.Grid.SubCellOffsets.Length; i++)
if (i != (int)preferredSubCell && !AnyActorsAt(uv, cell, layer, (SubCell)i, checkTransient))
if (i != (int)preferredSubCell && !AnyActorsAt(influenceNode, cell, (SubCell)i, checkTransient))
return (SubCell)i;

return SubCell.Invalid;
Expand All @@ -318,25 +316,20 @@
if (!layer.Contains(uv))
return preferredSubCell != SubCell.Any ? preferredSubCell : SubCell.First;

if (preferredSubCell != SubCell.Any && !AnyActorsAt(uv, layer, preferredSubCell, checkIfBlocker))
var influenceNode = layer[uv];
if (preferredSubCell != SubCell.Any && !AnyActorsAt(influenceNode, preferredSubCell, checkIfBlocker))
return preferredSubCell;

if (!AnyActorsAt(uv, layer))
if (influenceNode == null)
return map.Grid.DefaultSubCell;

for (var i = (byte)SubCell.First; i < map.Grid.SubCellOffsets.Length; i++)
if (i != (byte)preferredSubCell && !AnyActorsAt(uv, layer, (SubCell)i, checkIfBlocker))
if (i != (byte)preferredSubCell && !AnyActorsAt(influenceNode, (SubCell)i, checkIfBlocker))
return (SubCell)i;

return SubCell.Invalid;
}

// NOTE: pos required to be in map bounds
bool AnyActorsAt(MPos uv, CellLayer<InfluenceNode> layer)
{
return layer[uv] != null;
}

// NOTE: always includes transients with influence
public bool AnyActorsAt(CPos a)
{
Expand All @@ -345,14 +338,14 @@
if (!layer.Contains(uv))
return false;

return AnyActorsAt(uv, layer);
return layer[uv] != null;
}

// NOTE: pos required to be in map bounds
bool AnyActorsAt(MPos uv, CPos a, CellLayer<InfluenceNode> layer, SubCell sub, bool checkTransient)
bool AnyActorsAt(InfluenceNode influenceNode, CPos a, SubCell sub, bool checkTransient)
{
var always = sub == SubCell.FullCell || sub == SubCell.Any;
for (var i = layer[uv]; i != null; i = i.Next)
for (var i = influenceNode; i != null; i = i.Next)
{
if (always || i.SubCell == sub || i.SubCell == SubCell.FullCell)
{
Expand All @@ -376,14 +369,14 @@
if (!layer.Contains(uv))
return false;

return AnyActorsAt(uv, a, layer, sub, checkTransient);
return AnyActorsAt(layer[uv], a, sub, checkTransient);
}

// NOTE: can not check aircraft
bool AnyActorsAt(MPos uv, CellLayer<InfluenceNode> layer, SubCell sub, Func<Actor, bool> withCondition)
bool AnyActorsAt(InfluenceNode influenceNode, SubCell sub, Func<Actor, bool> withCondition)
{
var always = sub == SubCell.FullCell || sub == SubCell.Any;
for (var i = layer[uv]; i != null; i = i.Next)
for (var i = influenceNode; i != null; i = i.Next)
if ((always || i.SubCell == sub || i.SubCell == SubCell.FullCell) && withCondition(i.Actor))
return true;

Expand All @@ -398,7 +391,7 @@
if (!layer.Contains(uv))
return false;

return AnyActorsAt(uv, layer, sub, withCondition);
return AnyActorsAt(layer[uv], sub, withCondition);
}

public IEnumerable<Actor> AllActors()
Expand Down Expand Up @@ -441,9 +434,8 @@
if (!layer.Contains(uv))
continue;

var temp = layer[uv];
RemoveInfluenceInner(ref temp, self);
layer[uv] = temp;
if (RemoveInfluenceInner(layer[uv], self, out var head))
layer[uv] = head;

if (cellTriggerInfluence.TryGetValue(c.Cell, out var triggers))
foreach (var t in triggers)
Expand All @@ -453,15 +445,28 @@
}
}

static void RemoveInfluenceInner(ref InfluenceNode influenceNode, Actor toRemove)
static bool RemoveInfluenceInner(InfluenceNode influenceNode, Actor toRemove, out InfluenceNode head)
{
if (influenceNode == null)
return;
InfluenceNode prev = null;
for (var i = influenceNode; i != null; i = i.Next)
{
if (i.Actor == toRemove)
{
if (prev == null)
{
head = i.Next;
return true;
}

RemoveInfluenceInner(ref influenceNode.Next, toRemove);
prev.Next = i.Next;
break;
}

prev = i;
}

if (influenceNode.Actor == toRemove)
influenceNode = influenceNode.Next;
head = null;
return false;
}

public void UpdateOccupiedCells(IOccupySpace ios)
Expand All @@ -477,26 +482,24 @@
{
// Position updates are done in one pass
// to ensure consistency during a tick
if (removeActorPosition.Count > 0)
foreach (var (bin, actor) in removeActorPosition)

Check failure on line 485 in OpenRA.Mods.Common/Traits/World/ActorMap.cs

View workflow job for this annotation

GitHub Actions / Linux (.NET 6.0)

Unnecessary assignment of a value to 'bin'

Check failure on line 485 in OpenRA.Mods.Common/Traits/World/ActorMap.cs

View workflow job for this annotation

GitHub Actions / Linux (.NET 6.0)

Unnecessary assignment of a value to 'bin'

Check failure on line 485 in OpenRA.Mods.Common/Traits/World/ActorMap.cs

View workflow job for this annotation

GitHub Actions / Windows (.NET 6.0)

Unnecessary assignment of a value to 'bin'

Check failure on line 485 in OpenRA.Mods.Common/Traits/World/ActorMap.cs

View workflow job for this annotation

GitHub Actions / Windows (.NET 6.0)

Unnecessary assignment of a value to 'bin'

Check warning on line 485 in OpenRA.Mods.Common/Traits/World/ActorMap.cs

View workflow job for this annotation

GitHub Actions / Windows (.NET 6.0)

Unnecessary assignment of a value to 'bin'

Check warning on line 485 in OpenRA.Mods.Common/Traits/World/ActorMap.cs

View workflow job for this annotation

GitHub Actions / Windows (.NET 6.0)

Unnecessary assignment of a value to 'bin'
{
foreach (var bin in bins)
var bin = (Bin)actor.ActorMapPositionKey;

Check failure on line 487 in OpenRA.Mods.Common/Traits/World/ActorMap.cs

View workflow job for this annotation

GitHub Actions / Linux (.NET 6.0)

A local or parameter named 'bin' cannot be declared in this scope because that name is used in an enclosing local scope to define a local or parameter

Check failure on line 487 in OpenRA.Mods.Common/Traits/World/ActorMap.cs

View workflow job for this annotation

GitHub Actions / Linux (.NET 6.0)

A local or parameter named 'bin' cannot be declared in this scope because that name is used in an enclosing local scope to define a local or parameter

Check failure on line 487 in OpenRA.Mods.Common/Traits/World/ActorMap.cs

View workflow job for this annotation

GitHub Actions / Windows (.NET 6.0)

A local or parameter named 'bin' cannot be declared in this scope because that name is used in an enclosing local scope to define a local or parameter

Check failure on line 487 in OpenRA.Mods.Common/Traits/World/ActorMap.cs

View workflow job for this annotation

GitHub Actions / Windows (.NET 6.0)

A local or parameter named 'bin' cannot be declared in this scope because that name is used in an enclosing local scope to define a local or parameter

Check failure on line 487 in OpenRA.Mods.Common/Traits/World/ActorMap.cs

View workflow job for this annotation

GitHub Actions / Windows (.NET 6.0)

A local or parameter named 'bin' cannot be declared in this scope because that name is used in an enclosing local scope to define a local or parameter

Check failure on line 487 in OpenRA.Mods.Common/Traits/World/ActorMap.cs

View workflow job for this annotation

GitHub Actions / Windows (.NET 6.0)

A local or parameter named 'bin' cannot be declared in this scope because that name is used in an enclosing local scope to define a local or parameter
if (bin != null)
{
var removed = bin.Actors.RemoveAll(actorShouldBeRemoved);
if (removed > 0)
if (bin.Actors.Remove(actor))
foreach (var t in bin.ProximityTriggers)
t.Dirty = true;
}

removeActorPosition.Clear();
actor.ActorMapPositionKey = null;
}
}

removeActorPosition.Clear();

foreach (var a in addActorPosition)
{
var pos = a.CenterPosition;
var col = WorldCoordToBinIndex(pos.X).Clamp(0, cols - 1);
var row = WorldCoordToBinIndex(pos.Y).Clamp(0, rows - 1);
var bin = BinAt(row, col);

var bin = BinForActorPosition(a);
bin.Actors.Add(a);
foreach (var t in bin.ProximityTriggers)
t.Dirty = true;
Expand Down Expand Up @@ -595,7 +598,7 @@

public void RemovePosition(Actor a, IOccupySpace ios)
{
removeActorPosition.Add(a);
removeActorPosition.Add((BinForActorPosition(a), a));
}

public void UpdatePosition(Actor a, IOccupySpace ios)
Expand Down Expand Up @@ -623,6 +626,14 @@
return Rectangle.FromLTRB(minCol, minRow, maxCol, maxRow);
}

Bin BinForActorPosition(Actor a)
{
var pos = a.CenterPosition;
var col = WorldCoordToBinIndex(pos.X).Clamp(0, cols - 1);
var row = WorldCoordToBinIndex(pos.Y).Clamp(0, rows - 1);
return BinAt(row, col);
}

Bin BinAt(int binRow, int binCol)
{
return bins[binRow * cols + binCol];
Expand Down