From 82027dffd7cb1192c2e67d8971f16fc8f179ebe8 Mon Sep 17 00:00:00 2001 From: ikpil Date: Wed, 10 Apr 2024 19:52:58 +0900 Subject: [PATCH] Limiting the number of tasks during build --- .../Builder/TileNavMeshBuilder.cs | 3 +- src/DotRecast.Recast/RcBuilder.cs | 116 ++++++++---------- .../TestTiledNavMeshBuilder.cs | 3 +- .../RecastTileMeshTest.cs | 5 +- 4 files changed, 54 insertions(+), 73 deletions(-) diff --git a/src/DotRecast.Recast.Toolset/Builder/TileNavMeshBuilder.cs b/src/DotRecast.Recast.Toolset/Builder/TileNavMeshBuilder.cs index f73fbc9..89495b6 100644 --- a/src/DotRecast.Recast.Toolset/Builder/TileNavMeshBuilder.cs +++ b/src/DotRecast.Recast.Toolset/Builder/TileNavMeshBuilder.cs @@ -97,7 +97,8 @@ namespace DotRecast.Recast.Toolset.Builder filterLowHangingObstacles, filterLedgeSpans, filterWalkableLowHeightSpans, SampleAreaModifications.SAMPLE_AREAMOD_WALKABLE, true); RcBuilder rcBuilder = new RcBuilder(); - return rcBuilder.BuildTiles(geom, cfg, Task.Factory); + var task = rcBuilder.BuildTilesAsync(geom, cfg, Environment.ProcessorCount + 1, Task.Factory); + return task.Result; } public DtNavMesh BuildNavMesh(IInputGeomProvider geom, List meshData, float cellSize, int tileSize, int vertsPerPoly) diff --git a/src/DotRecast.Recast/RcBuilder.cs b/src/DotRecast.Recast/RcBuilder.cs index 4c91cf8..129cf2f 100644 --- a/src/DotRecast.Recast/RcBuilder.cs +++ b/src/DotRecast.Recast/RcBuilder.cs @@ -19,7 +19,9 @@ freely, subject to the following restrictions: */ using System; +using System.Collections.Concurrent; using System.Collections.Generic; +using System.Linq; using System.Threading; using System.Threading.Tasks; using DotRecast.Core; @@ -45,115 +47,93 @@ namespace DotRecast.Recast _progressListener = progressListener; } - public List BuildTiles(IInputGeomProvider geom, RcConfig cfg, TaskFactory taskFactory) + public Task> BuildTilesAsync(IInputGeomProvider geom, RcConfig cfg, + int threads = 0, TaskFactory taskFactory = null, CancellationToken cancellation = default) { RcVec3f bmin = geom.GetMeshBoundsMin(); RcVec3f bmax = geom.GetMeshBoundsMax(); CalcTileCount(bmin, bmax, cfg.Cs, cfg.TileSizeX, cfg.TileSizeZ, out var tw, out var th); - List results = new List(); - if (null != taskFactory) + + if (1 < threads) { - BuildMultiThreadAsync(geom, cfg, bmin, bmax, tw, th, results, taskFactory, default); + return BuildMultiThreadAsync(geom, cfg, bmin, bmax, tw, th, threads, taskFactory ?? Task.Factory, cancellation); } - else + + var results = BuildSingleThread(geom, cfg, bmin, bmax, tw, th); + return Task.FromResult(results); + } + + private List BuildSingleThread(IInputGeomProvider geom, RcConfig cfg, RcVec3f bmin, RcVec3f bmax, int tw, int th) + { + var results = new List(th * tw); + RcAtomicInteger counter = new RcAtomicInteger(0); + + for (int y = 0; y < th; ++y) { - BuildSingleThreadAsync(geom, cfg, bmin, bmax, tw, th, results); + for (int x = 0; x < tw; ++x) + { + var result = BuildTile(geom, cfg, bmin, bmax, x, y, counter, tw * th); + results.Add(result); + } } return results; } - - public Task BuildTilesAsync(IInputGeomProvider geom, RcConfig cfg, int threads, List results, TaskFactory taskFactory, CancellationToken cancellationToken) + private async Task> BuildMultiThreadAsync(IInputGeomProvider geom, RcConfig cfg, RcVec3f bmin, RcVec3f bmax, int tw, int th, + int threads, TaskFactory taskFactory, CancellationToken cancellation) { - RcVec3f bmin = geom.GetMeshBoundsMin(); - RcVec3f bmax = geom.GetMeshBoundsMax(); - CalcTileCount(bmin, bmax, cfg.Cs, cfg.TileSizeX, cfg.TileSizeZ, out var tw, out var th); - Task task; - if (1 < threads) - { - task = BuildMultiThreadAsync(geom, cfg, bmin, bmax, tw, th, results, taskFactory, cancellationToken); - } - else - { - task = BuildSingleThreadAsync(geom, cfg, bmin, bmax, tw, th, results); - } - - return task; - } - - private Task BuildSingleThreadAsync(IInputGeomProvider geom, RcConfig cfg, RcVec3f bmin, RcVec3f bmax, - int tw, int th, List results) - { - RcAtomicInteger counter = new RcAtomicInteger(0); - for (int y = 0; y < th; ++y) - { - for (int x = 0; x < tw; ++x) - { - results.Add(BuildTile(geom, cfg, bmin, bmax, x, y, counter, tw * th)); - } - } - - return Task.CompletedTask; - } - - private Task BuildMultiThreadAsync(IInputGeomProvider geom, RcConfig cfg, RcVec3f bmin, RcVec3f bmax, - int tw, int th, List results, TaskFactory taskFactory, CancellationToken cancellationToken) - { - RcAtomicInteger counter = new RcAtomicInteger(0); - CountdownEvent latch = new CountdownEvent(tw * th); - List tasks = new List(); + var results = new ConcurrentQueue(); + RcAtomicInteger progress = new RcAtomicInteger(0); + List limits = new List(threads); for (int x = 0; x < tw; ++x) { for (int y = 0; y < th; ++y) { int tx = x; int ty = y; - var task = taskFactory.StartNew(() => + var task = taskFactory.StartNew(state => { - if (cancellationToken.IsCancellationRequested) + if (cancellation.IsCancellationRequested) return; try { - RcBuilderResult tile = BuildTile(geom, cfg, bmin, bmax, tx, ty, counter, tw * th); - lock (results) - { - results.Add(tile); - } + RcBuilderResult result = BuildTile(geom, cfg, bmin, bmax, tx, ty, progress, tw * th); + results.Enqueue(result); } catch (Exception e) { Console.WriteLine(e); } + }, null); - - latch.Signal(); - }, cancellationToken); - - tasks.Add(task); + limits.Add(task); + if (threads <= limits.Count) + { + await Task.WhenAll(limits); + limits.Clear(); + } } } - try - { - latch.Wait(); - } - catch (ThreadInterruptedException) + if (0 < limits.Count) { + await Task.WhenAll(limits); + limits.Clear(); } - return Task.WhenAll(tasks.ToArray()); + var list = results.ToList(); + return list; } - public RcBuilderResult BuildTile(IInputGeomProvider geom, RcConfig cfg, RcVec3f bmin, RcVec3f bmax, int tx, - int ty, RcAtomicInteger counter, int total) + public RcBuilderResult BuildTile(IInputGeomProvider geom, RcConfig cfg, RcVec3f bmin, RcVec3f bmax, int tx, int ty, RcAtomicInteger progress, int total) { RcBuilderResult result = Build(geom, new RcBuilderConfig(cfg, bmin, bmax, tx, ty)); if (_progressListener != null) { - _progressListener.OnProgress(counter.IncrementAndGet(), total); + _progressListener.OnProgress(progress.IncrementAndGet(), total); } return result; @@ -205,7 +185,7 @@ namespace DotRecast.Recast { // Prepare for region partitioning, by calculating distance field along the walkable surface. RcRegions.BuildDistanceField(ctx, chf); - + // Partition the walkable surface into simple regions without holes. RcRegions.BuildRegions(ctx, chf, cfg.MinRegionArea, cfg.MergeRegionArea); } @@ -298,7 +278,7 @@ namespace DotRecast.Recast RcHeightfield solid = RcVoxelizations.BuildSolidHeightfield(ctx, geom, builderCfg); FilterHeightfield(ctx, solid, builderCfg.cfg); RcCompactHeightfield chf = BuildCompactHeightfield(ctx, geom, builderCfg.cfg, solid); - + RcLayers.BuildHeightfieldLayers(ctx, chf, builderCfg.cfg.BorderSize, builderCfg.cfg.WalkableHeight, out var lset); return lset; } diff --git a/test/DotRecast.Detour.Test/TestTiledNavMeshBuilder.cs b/test/DotRecast.Detour.Test/TestTiledNavMeshBuilder.cs index 6baef44..e7bf7fd 100644 --- a/test/DotRecast.Detour.Test/TestTiledNavMeshBuilder.cs +++ b/test/DotRecast.Detour.Test/TestTiledNavMeshBuilder.cs @@ -78,7 +78,8 @@ public class TestTiledNavMeshBuilder true, true, true, SampleAreaModifications.SAMPLE_AREAMOD_GROUND, true); RcBuilder rcBuilder = new RcBuilder(); - List rcResult = rcBuilder.BuildTiles(geom, cfg, null); + var task = rcBuilder.BuildTilesAsync(geom, cfg); + List rcResult = task.Result; // Add tiles to nav mesh diff --git a/test/DotRecast.Recast.Test/RecastTileMeshTest.cs b/test/DotRecast.Recast.Test/RecastTileMeshTest.cs index bacb9ae..d8a4fe7 100644 --- a/test/DotRecast.Recast.Test/RecastTileMeshTest.cs +++ b/test/DotRecast.Recast.Test/RecastTileMeshTest.cs @@ -27,7 +27,6 @@ using NUnit.Framework; namespace DotRecast.Recast.Test; - public class RecastTileMeshTest { private const float m_cellSize = 0.3f; @@ -138,8 +137,8 @@ public class RecastTileMeshTest private void Build(IInputGeomProvider geom, RcBuilder builder, RcConfig cfg, int threads, bool validate) { CancellationTokenSource cts = new CancellationTokenSource(); - List tiles = new(); - var task = builder.BuildTilesAsync(geom, cfg, threads, tiles, Task.Factory, cts.Token); + var task = builder.BuildTilesAsync(geom, cfg, threads, Task.Factory, cts.Token); + List tiles = task.Result; if (validate) { RcBuilderResult rcResult = GetTile(tiles, 7, 8);