Re-implement parallel for pathfinding crumbs (#12355)
This commit is contained in:
@@ -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>();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -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);
|
||||||
|
|||||||
@@ -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!;
|
||||||
|
|||||||
Reference in New Issue
Block a user