Skip to content

Commit

Permalink
Reduce GC pressure.
Browse files Browse the repository at this point in the history
  • Loading branch information
awgil committed Jun 7, 2024
1 parent 010ded5 commit 3ad46ec
Show file tree
Hide file tree
Showing 7 changed files with 120 additions and 81 deletions.
9 changes: 5 additions & 4 deletions BossMod/AI/AIBehaviour.cs
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ namespace BossMod.AI;
sealed class AIBehaviour(AIController ctrl, Autorotation autorot) : IDisposable
{
private readonly AIConfig _config = Service.Config.Get<AIConfig>();
private readonly NavigationDecision.Context _naviCtx = new();
private NavigationDecision _naviDecision;
private bool _forbidMovement;
private bool _forbidActions;
Expand Down Expand Up @@ -104,9 +105,9 @@ private NavigationDecision BuildNavigationDecision(Actor player, Actor master, r
if (_forbidMovement)
return new() { LeewaySeconds = float.MaxValue };
if (_followMaster)
return NavigationDecision.Build(autorot.WorldState, autorot.Hints, player, master.Position, 1, new(), Positional.Any);
return NavigationDecision.Build(_naviCtx, autorot.WorldState, autorot.Hints, player, master.Position, 1, new(), Positional.Any);
if (targeting.Target == null)
return NavigationDecision.Build(autorot.WorldState, autorot.Hints, player, null, 0, new(), Positional.Any);
return NavigationDecision.Build(_naviCtx, autorot.WorldState, autorot.Hints, player, null, 0, new(), Positional.Any);

var adjRange = targeting.PreferredRange + player.HitboxRadius + targeting.Target.Actor.HitboxRadius;
if (targeting.PreferTanking)
Expand All @@ -117,12 +118,12 @@ private NavigationDecision BuildNavigationDecision(Actor player, Actor master, r
if (desiredToTarget.LengthSq() > 4 /*&& (_autorot.ClassActions?.GetState().GCD ?? 0) > 0.5f*/)
{
var dest = autorot.Hints.ClampToBounds(targeting.Target.DesiredPosition - adjRange * desiredToTarget.Normalized());
return NavigationDecision.Build(autorot.WorldState, autorot.Hints, player, dest, 0.5f, new(), Positional.Any);
return NavigationDecision.Build(_naviCtx, autorot.WorldState, autorot.Hints, player, dest, 0.5f, new(), Positional.Any);
}
}

var adjRotation = targeting.PreferTanking ? targeting.Target.DesiredRotation : targeting.Target.Actor.Rotation;
return NavigationDecision.Build(autorot.WorldState, autorot.Hints, player, targeting.Target.Actor.Position, adjRange, adjRotation, targeting.PreferredPosition);
return NavigationDecision.Build(_naviCtx, autorot.WorldState, autorot.Hints, player, targeting.Target.Actor.Position, adjRange, adjRotation, targeting.PreferredPosition);
}

private void FocusMaster(Actor master)
Expand Down
13 changes: 8 additions & 5 deletions BossMod/BossModule/AIHintsVisualizer.cs
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ public class AIHintsVisualizer(AIHints hints, WorldState ws, Actor player, ulong
{
private readonly MapVisualizer?[] _zoneVisualizers = new MapVisualizer?[hints.ForbiddenZones.Count];
private MapVisualizer? _pathfindVisualizer;
private NavigationDecision.Context _naviCtx = new();

Check failure on line 10 in BossMod/BossModule/AIHintsVisualizer.cs

View workflow job for this annotation

GitHub Actions / Build

Make field readonly (https://learn.microsoft.com/dotnet/fundamentals/code-analysis/style-rules/ide0044)

Check failure on line 10 in BossMod/BossModule/AIHintsVisualizer.cs

View workflow job for this annotation

GitHub Actions / Build

Make field readonly (https://learn.microsoft.com/dotnet/fundamentals/code-analysis/style-rules/ide0044)
private NavigationDecision _navi;

public void Draw(UITree tree)
Expand Down Expand Up @@ -49,7 +50,8 @@ public void Draw(UITree tree)

private MapVisualizer BuildZoneVisualizer(Func<WPos, float> shape)
{
var map = hints.Bounds.PathfindMap(hints.Center);
var map = new Map();
hints.Bounds.PathfindMap(map, hints.Center);
map.BlockPixelsInside(shape, 0, NavigationDecision.DefaultForbiddenZoneCushion);
return new MapVisualizer(map, 0, player.Position);
}
Expand All @@ -61,7 +63,8 @@ private MapVisualizer BuildPathfindingVisualizer()
_navi = BuildPathfind(targeting.enemy, targeting.range, targeting.pos, targeting.tank);
if (_navi.Map == null)
{
_navi.Map = hints.Bounds.PathfindMap(hints.Center);
_navi.Map = new();
hints.Bounds.PathfindMap(_navi.Map, hints.Center);
var imm = NavigationDecision.ImminentExplosionTime(ws.CurrentTime);
foreach (var (shape, activation) in hints.ForbiddenZones)
NavigationDecision.AddBlockerZone(_navi.Map, imm, activation, shape, NavigationDecision.DefaultForbiddenZoneCushion);
Expand All @@ -74,7 +77,7 @@ private MapVisualizer BuildPathfindingVisualizer()
private NavigationDecision BuildPathfind(AIHints.Enemy? target, float range, Positional positional, bool preferTanking)
{
if (target == null)
return NavigationDecision.Build(ws, hints, player, null, 0, new(), Positional.Any);
return NavigationDecision.Build(_naviCtx, ws, hints, player, null, 0, new(), Positional.Any);

var adjRange = range + player.HitboxRadius + target.Actor.HitboxRadius;
if (preferTanking)
Expand All @@ -85,11 +88,11 @@ private NavigationDecision BuildPathfind(AIHints.Enemy? target, float range, Pos
if (desiredToTarget.LengthSq() > 4 /* && gcd check*/)
{
var dest = target.DesiredPosition - adjRange * desiredToTarget.Normalized();
return NavigationDecision.Build(ws, hints, player, dest, 0.5f, new(), Positional.Any);
return NavigationDecision.Build(_naviCtx, ws, hints, player, dest, 0.5f, new(), Positional.Any);
}
}

var adjRotation = preferTanking ? target.DesiredRotation : target.Actor.Rotation;
return NavigationDecision.Build(ws, hints, player, target.Actor.Position, adjRange, adjRotation, positional);
return NavigationDecision.Build(_naviCtx, ws, hints, player, target.Actor.Position, adjRange, adjRotation, positional);
}
}
10 changes: 5 additions & 5 deletions BossMod/BossModule/ArenaBounds.cs
Original file line number Diff line number Diff line change
Expand Up @@ -31,7 +31,7 @@ public float ScreenHalfSize
}

protected abstract PolygonClipper.Operand BuildClipPoly();
public abstract Pathfinding.Map PathfindMap(WPos center); // if implementation caches a map, it should return a clone
public abstract void PathfindMap(Pathfinding.Map map, WPos center);
public abstract bool Contains(WDir offset);
public abstract float IntersectRay(WDir originOffset, WDir dir);
public abstract WDir ClampToBounds(WDir offset);
Expand Down Expand Up @@ -103,7 +103,7 @@ public record class ArenaBoundsCircle(float Radius, float MapResolution = 0.5f)
private Pathfinding.Map? _cachedMap;

protected override PolygonClipper.Operand BuildClipPoly() => new(CurveApprox.Circle(Radius, MaxApproxError));
public override Pathfinding.Map PathfindMap(WPos center) => (_cachedMap ??= BuildMap()).Clone(center);
public override void PathfindMap(Pathfinding.Map map, WPos center) => map.Init(_cachedMap ??= BuildMap(), center);
public override bool Contains(WDir offset) => offset.LengthSq() <= Radius * Radius;
public override float IntersectRay(WDir originOffset, WDir dir) => Intersect.RayCircle(originOffset, dir, Radius);

Expand All @@ -127,7 +127,7 @@ public record class ArenaBoundsSquare(float Radius, float MapResolution = 0.5f)
public float HalfWidth => Radius;

protected override PolygonClipper.Operand BuildClipPoly() => new(CurveApprox.Rect(new(Radius, 0), new(0, Radius)));
public override Pathfinding.Map PathfindMap(WPos center) => new(MapResolution, center, Radius, Radius);
public override void PathfindMap(Pathfinding.Map map, WPos center) => map.Init(MapResolution, center, Radius, Radius);
public override bool Contains(WDir offset) => offset.AlmostZero(Radius);
public override float IntersectRay(WDir originOffset, WDir dir) => Intersect.RayAABB(originOffset, dir, Radius, Radius);

Expand All @@ -147,7 +147,7 @@ public record class ArenaBoundsRect(float HalfWidth, float HalfHeight, Angle Rot
public readonly WDir Orientation = Rotation.ToDirection();

protected override PolygonClipper.Operand BuildClipPoly() => new(CurveApprox.Rect(Orientation, HalfWidth, HalfHeight));
public override Pathfinding.Map PathfindMap(WPos center) => new(MapResolution, center, HalfWidth, HalfHeight, Rotation);
public override void PathfindMap(Pathfinding.Map map, WPos center) => map.Init(MapResolution, center, HalfWidth, HalfHeight, Rotation);
public override bool Contains(WDir offset) => offset.InRect(Orientation, HalfHeight, HalfHeight, HalfWidth);
public override float IntersectRay(WDir originOffset, WDir dir) => Intersect.RayRect(originOffset, dir, Orientation, HalfWidth, HalfHeight);

Expand All @@ -169,7 +169,7 @@ public record class ArenaBoundsCustom(float Radius, RelSimplifiedComplexPolygon
private Pathfinding.Map? _cachedMap;

protected override PolygonClipper.Operand BuildClipPoly() => new(Poly);
public override Pathfinding.Map PathfindMap(WPos center) => (_cachedMap ??= BuildMap()).Clone(center);
public override void PathfindMap(Pathfinding.Map map, WPos center) => map.Init(_cachedMap ??= BuildMap(), center);
public override bool Contains(WDir offset) => Poly.Contains(offset);
public override float IntersectRay(WDir originOffset, WDir dir) => Intersect.RayPolygon(originOffset, dir, Poly);
public override WDir ClampToBounds(WDir offset)
Expand Down
48 changes: 34 additions & 14 deletions BossMod/Pathfinding/Map.cs
Original file line number Diff line number Diff line change
Expand Up @@ -16,14 +16,14 @@ public struct Pixel
public int Priority; // >0 if goal
}

public float Resolution { get; private init; } // pixel size, in world units
public int Width { get; private init; } // always even
public int Height { get; private init; } // always even
public Pixel[] Pixels;
public float Resolution { get; private set; } // pixel size, in world units
public int Width { get; private set; } // always even
public int Height { get; private set; } // always even
public Pixel[] Pixels = [];

public WPos Center { get; private set; } // position of map center in world units
public Angle Rotation { get; private init; } // rotation relative to world space (=> ToDirection() is equal to direction of local 'height' axis in world space)
private WDir LocalZDivRes { get; init; }
public Angle Rotation { get; private set; } // rotation relative to world space (=> ToDirection() is equal to direction of local 'height' axis in world space)
private WDir LocalZDivRes { get; set; }

public float MaxG { get; private set; } // maximal 'maxG' value of all blocked pixels
public int MaxPriority { get; private set; } // maximal 'priority' value of all goal pixels
Expand All @@ -32,25 +32,45 @@ public struct Pixel

public Pixel this[int x, int y] => InBounds(x, y) ? Pixels[y * Width + x] : new() { MaxG = float.MaxValue, Priority = 0 };

public Map(float resolution, WPos center, float worldHalfWidth, float worldHalfHeight, Angle rotation = new())
public Map() { }
public Map(float resolution, WPos center, float worldHalfWidth, float worldHalfHeight, Angle rotation = new()) => Init(resolution, center, worldHalfWidth, worldHalfHeight, rotation);

public void Init(float resolution, WPos center, float worldHalfWidth, float worldHalfHeight, Angle rotation = new())
{
Resolution = resolution;
Width = 2 * (int)MathF.Ceiling(worldHalfWidth / resolution);
Height = 2 * (int)MathF.Ceiling(worldHalfHeight / resolution);
Pixels = Utils.MakeArray(Width * Height, new Pixel() { MaxG = float.MaxValue, Priority = 0 });

var numPixels = Width * Height;
if (Pixels.Length < numPixels)
Pixels = new Pixel[numPixels];
Array.Fill(Pixels, new Pixel { MaxG = float.MaxValue, Priority = 0 }, 0, numPixels);

Center = center;
Rotation = rotation;
LocalZDivRes = rotation.ToDirection() / Resolution;

MaxG = 0;
MaxPriority = 0;
}

public Map Clone(WPos center)
public void Init(Map source, WPos center)
{
var res = (Map)MemberwiseClone();
res.Pixels = new Pixel[Pixels.Length];
Array.Copy(Pixels, res.Pixels, Pixels.Length);
res.Center = center;
return res;
Resolution = source.Resolution;
Width = source.Width;
Height = source.Height;

var numPixels = Width * Height;
if (Pixels.Length < numPixels)
Pixels = new Pixel[numPixels];
Array.Copy(source.Pixels, Pixels, numPixels);

Center = center;
Rotation = source.Rotation;
LocalZDivRes = source.LocalZDivRes;

MaxG = source.MaxG;
MaxPriority = source.MaxPriority;
}

public Vector2 WorldToGridFrac(WPos world)
Expand Down
7 changes: 6 additions & 1 deletion BossMod/Pathfinding/MapVisualizer.cs
Original file line number Diff line number Diff line change
Expand Up @@ -205,5 +205,10 @@ private void DrawPath(ImDrawListPtr dl, Vector2 tl, int startingIndex)
}
}

private ThetaStar BuildPathfind() => new(Map, GoalPriority, StartPos, 1.0f / 6);
private ThetaStar BuildPathfind()
{
var res = new ThetaStar();
res.Start(Map, GoalPriority, StartPos, 1.0f / 6);
return res;
}
}
Loading

0 comments on commit 3ad46ec

Please sign in to comment.