Re-implement parallel for pathfinding crumbs (#12355)

This commit is contained in:
metalgearsloth
2022-11-22 13:36:34 +11:00
committed by GitHub
parent 039aad4710
commit fee44d13b2
3 changed files with 60 additions and 45 deletions

View File

@@ -13,6 +13,11 @@ public sealed class GridPathfindingChunk
public readonly List<PathPoly>[] Polygons = new List<PathPoly>[SharedPathfindingSystem.ChunkSize * SharedPathfindingSystem.ChunkSize]; public readonly List<PathPoly>[] Polygons = new List<PathPoly>[SharedPathfindingSystem.ChunkSize * SharedPathfindingSystem.ChunkSize];
/// <summary>
/// Store the recalculated polygons to know what needs changing.
/// </summary>
internal readonly List<PathPoly>[] BufferPolygons = new List<PathPoly>[SharedPathfindingSystem.ChunkSize * SharedPathfindingSystem.ChunkSize];
/// <summary> /// <summary>
/// The relevant polygon for this chunk's portals /// The relevant polygon for this chunk's portals
/// </summary> /// </summary>
@@ -28,6 +33,7 @@ public sealed class GridPathfindingChunk
for (var x = 0; x < Polygons.Length; x++) for (var x = 0; x < Polygons.Length; x++)
{ {
Polygons[x] = new List<PathPoly>(); Polygons[x] = new List<PathPoly>();
BufferPolygons[x] = new List<PathPoly>();
} }
} }
} }

View File

@@ -84,6 +84,10 @@ public sealed partial class PathfindingSystem
var updateCount = 0; var updateCount = 0;
#endif #endif
_stopwatch.Restart(); _stopwatch.Restart();
var options = new ParallelOptions()
{
MaxDegreeOfParallelism = _parallel.ParallelProcessCount,
};
// We defer chunk updates because rebuilding a navmesh is hella costly // We defer chunk updates because rebuilding a navmesh is hella costly
// If we're paused then NPCs can't run anyway. // If we're paused then NPCs can't run anyway.
@@ -132,7 +136,7 @@ public sealed partial class PathfindingSystem
// This is for map <> grid pathfinding // This is for map <> grid pathfinding
// Without parallel this is roughly 3x slower on my desktop. // Without parallel this is roughly 3x slower on my desktop.
for (var i = 0; i < dirt.Length; i++) Parallel.For(0, dirt.Length, options, i =>
{ {
// Doing the queries per task seems faster. // Doing the queries per task seems faster.
var accessQuery = GetEntityQuery<AccessReaderComponent>(); var accessQuery = GetEntityQuery<AccessReaderComponent>();
@@ -141,8 +145,9 @@ public sealed partial class PathfindingSystem
var fixturesQuery = GetEntityQuery<FixturesComponent>(); var fixturesQuery = GetEntityQuery<FixturesComponent>();
var physicsQuery = GetEntityQuery<PhysicsComponent>(); var physicsQuery = GetEntityQuery<PhysicsComponent>();
var xformQuery = GetEntityQuery<TransformComponent>(); var xformQuery = GetEntityQuery<TransformComponent>();
BuildBreadcrumbs(dirt[i], mapGridComp, accessQuery, destructibleQuery, doorQuery, fixturesQuery, physicsQuery, xformQuery); BuildBreadcrumbs(dirt[i], mapGridComp, accessQuery, destructibleQuery, doorQuery, fixturesQuery,
} physicsQuery, xformQuery);
});
const int Division = 4; const int Division = 4;
@@ -159,7 +164,7 @@ public sealed partial class PathfindingSystem
{ {
var it1 = it; var it1 = it;
Parallel.For(0, dirt.Length, j => Parallel.For(0, dirt.Length, options, j =>
{ {
var chunk = dirt[j]; var chunk = dirt[j];
// Check if the chunk is safe on this iteration. // Check if the chunk is safe on this iteration.
@@ -375,13 +380,11 @@ public sealed partial class PathfindingSystem
var points = chunk.Points; var points = chunk.Points;
var gridOrigin = chunk.Origin * ChunkSize; var gridOrigin = chunk.Origin * ChunkSize;
var tileEntities = new ValueList<EntityUid>(); var tileEntities = new ValueList<EntityUid>();
var chunkPolys = chunk.BufferPolygons;
// TODO: Pool this or something
var chunkPolys = new List<PathPoly>[ChunkSize * ChunkSize];
for (var i = 0; i < chunkPolys.Length; i++) for (var i = 0; i < chunkPolys.Length; i++)
{ {
chunkPolys[i] = new List<PathPoly>(); chunkPolys[i].Clear();
} }
var tilePolys = new ValueList<Box2i>(SubStep); var tilePolys = new ValueList<Box2i>(SubStep);
@@ -577,43 +580,6 @@ public sealed partial class PathfindingSystem
} }
} }
// Check if the tiles match
for (var x = 0; x < ChunkSize; x++)
{
for (var y = 0; y < ChunkSize; y++)
{
var index = x * ChunkSize + y;
var polys = chunkPolys[index];
var existing = chunk.Polygons[index];
var isEquivalent = true;
if (polys.Count == existing.Count)
{
// May want to update damage or the likes if it's different but not invalidate the ref.
for (var i = 0; i < existing.Count; i++)
{
var ePoly = existing[i];
var poly = polys[i];
if (!ePoly.IsEquivalent(poly))
{
isEquivalent = false;
break;
}
ePoly.Data.Damage = poly.Data.Damage;
}
if (isEquivalent)
continue;
}
ClearTilePolys(existing);
existing.AddRange(polys);
}
}
// _sawmill.Debug($"Built breadcrumbs in {sw.Elapsed.TotalMilliseconds}ms"); // _sawmill.Debug($"Built breadcrumbs in {sw.Elapsed.TotalMilliseconds}ms");
SendBreadcrumbs(chunk, grid.GridEntityId); SendBreadcrumbs(chunk, grid.GridEntityId);
} }
@@ -650,7 +616,48 @@ public sealed partial class PathfindingSystem
{ {
var sw = new Stopwatch(); var sw = new Stopwatch();
sw.Start(); sw.Start();
// After the breadcrumbs step need to determine which polygons need rebuilding. Can't do this above
// as we are tampering with neighbor nodes.
var chunkPolys = chunk.Polygons; var chunkPolys = chunk.Polygons;
var bufferPolygons = chunk.BufferPolygons;
for (var x = 0; x < ChunkSize; x++)
{
for (var y = 0; y < ChunkSize; y++)
{
var index = x * ChunkSize + y;
var polys = bufferPolygons[index];
var existing = chunkPolys[index];
var isEquivalent = true;
if (polys.Count == existing.Count)
{
// May want to update damage or the likes if it's different but not invalidate the ref.
for (var i = 0; i < existing.Count; i++)
{
var ePoly = existing[i];
var poly = polys[i];
if (!ePoly.IsEquivalent(poly))
{
isEquivalent = false;
break;
}
ePoly.Data.Damage = poly.Data.Damage;
}
if (isEquivalent)
continue;
}
ClearTilePolys(existing);
existing.AddRange(polys);
}
}
component.Chunks.TryGetValue(chunk.Origin + new Vector2i(-1, 0), out var leftChunk); component.Chunks.TryGetValue(chunk.Origin + new Vector2i(-1, 0), out var leftChunk);
component.Chunks.TryGetValue(chunk.Origin + new Vector2i(0, -1), out var bottomChunk); component.Chunks.TryGetValue(chunk.Origin + new Vector2i(0, -1), out var bottomChunk);
component.Chunks.TryGetValue(chunk.Origin + new Vector2i(1, 0), out var rightChunk); component.Chunks.TryGetValue(chunk.Origin + new Vector2i(1, 0), out var rightChunk);

View File

@@ -15,6 +15,7 @@ using Robust.Shared.Physics.Components;
using Robust.Shared.Physics.Systems; using Robust.Shared.Physics.Systems;
using Robust.Shared.Players; using Robust.Shared.Players;
using Robust.Shared.Random; using Robust.Shared.Random;
using Robust.Shared.Threading;
using Robust.Shared.Timing; using Robust.Shared.Timing;
using Robust.Shared.Utility; using Robust.Shared.Utility;
@@ -37,6 +38,7 @@ namespace Content.Server.NPC.Pathfinding
[Dependency] private readonly IAdminManager _adminManager = default!; [Dependency] private readonly IAdminManager _adminManager = default!;
[Dependency] private readonly IGameTiming _timing = default!; [Dependency] private readonly IGameTiming _timing = default!;
[Dependency] private readonly IParallelManager _parallel = default!;
[Dependency] private readonly IPlayerManager _playerManager = default!; [Dependency] private readonly IPlayerManager _playerManager = default!;
[Dependency] private readonly IRobustRandom _random = default!; [Dependency] private readonly IRobustRandom _random = default!;
[Dependency] private readonly DestructibleSystem _destructible = default!; [Dependency] private readonly DestructibleSystem _destructible = default!;