Limiting the number of tasks during build

This commit is contained in:
ikpil 2024-04-10 19:52:58 +09:00
parent 573386c473
commit 82027dffd7
4 changed files with 54 additions and 73 deletions

View File

@ -97,7 +97,8 @@ namespace DotRecast.Recast.Toolset.Builder
filterLowHangingObstacles, filterLedgeSpans, filterWalkableLowHeightSpans, filterLowHangingObstacles, filterLedgeSpans, filterWalkableLowHeightSpans,
SampleAreaModifications.SAMPLE_AREAMOD_WALKABLE, true); SampleAreaModifications.SAMPLE_AREAMOD_WALKABLE, true);
RcBuilder rcBuilder = new RcBuilder(); 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<DtMeshData> meshData, float cellSize, int tileSize, int vertsPerPoly) public DtNavMesh BuildNavMesh(IInputGeomProvider geom, List<DtMeshData> meshData, float cellSize, int tileSize, int vertsPerPoly)

View File

@ -19,7 +19,9 @@ freely, subject to the following restrictions:
*/ */
using System; using System;
using System.Collections.Concurrent;
using System.Collections.Generic; using System.Collections.Generic;
using System.Linq;
using System.Threading; using System.Threading;
using System.Threading.Tasks; using System.Threading.Tasks;
using DotRecast.Core; using DotRecast.Core;
@ -45,115 +47,93 @@ namespace DotRecast.Recast
_progressListener = progressListener; _progressListener = progressListener;
} }
public List<RcBuilderResult> BuildTiles(IInputGeomProvider geom, RcConfig cfg, TaskFactory taskFactory) public Task<List<RcBuilderResult>> BuildTilesAsync(IInputGeomProvider geom, RcConfig cfg,
int threads = 0, TaskFactory taskFactory = null, CancellationToken cancellation = default)
{ {
RcVec3f bmin = geom.GetMeshBoundsMin(); RcVec3f bmin = geom.GetMeshBoundsMin();
RcVec3f bmax = geom.GetMeshBoundsMax(); RcVec3f bmax = geom.GetMeshBoundsMax();
CalcTileCount(bmin, bmax, cfg.Cs, cfg.TileSizeX, cfg.TileSizeZ, out var tw, out var th); CalcTileCount(bmin, bmax, cfg.Cs, cfg.TileSizeX, cfg.TileSizeZ, out var tw, out var th);
List<RcBuilderResult> results = new List<RcBuilderResult>();
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<RcBuilderResult> BuildSingleThread(IInputGeomProvider geom, RcConfig cfg, RcVec3f bmin, RcVec3f bmax, int tw, int th)
{
var results = new List<RcBuilderResult>(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; return results;
} }
private async Task<List<RcBuilderResult>> BuildMultiThreadAsync(IInputGeomProvider geom, RcConfig cfg, RcVec3f bmin, RcVec3f bmax, int tw, int th,
public Task BuildTilesAsync(IInputGeomProvider geom, RcConfig cfg, int threads, List<RcBuilderResult> results, TaskFactory taskFactory, CancellationToken cancellationToken) int threads, TaskFactory taskFactory, CancellationToken cancellation)
{ {
RcVec3f bmin = geom.GetMeshBoundsMin(); var results = new ConcurrentQueue<RcBuilderResult>();
RcVec3f bmax = geom.GetMeshBoundsMax(); RcAtomicInteger progress = new RcAtomicInteger(0);
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<RcBuilderResult> 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<RcBuilderResult> results, TaskFactory taskFactory, CancellationToken cancellationToken)
{
RcAtomicInteger counter = new RcAtomicInteger(0);
CountdownEvent latch = new CountdownEvent(tw * th);
List<Task> tasks = new List<Task>();
List<Task> limits = new List<Task>(threads);
for (int x = 0; x < tw; ++x) for (int x = 0; x < tw; ++x)
{ {
for (int y = 0; y < th; ++y) for (int y = 0; y < th; ++y)
{ {
int tx = x; int tx = x;
int ty = y; int ty = y;
var task = taskFactory.StartNew(() => var task = taskFactory.StartNew(state =>
{ {
if (cancellationToken.IsCancellationRequested) if (cancellation.IsCancellationRequested)
return; return;
try try
{ {
RcBuilderResult tile = BuildTile(geom, cfg, bmin, bmax, tx, ty, counter, tw * th); RcBuilderResult result = BuildTile(geom, cfg, bmin, bmax, tx, ty, progress, tw * th);
lock (results) results.Enqueue(result);
{
results.Add(tile);
}
} }
catch (Exception e) catch (Exception e)
{ {
Console.WriteLine(e); Console.WriteLine(e);
} }
}, null);
limits.Add(task);
latch.Signal(); if (threads <= limits.Count)
}, cancellationToken); {
await Task.WhenAll(limits);
tasks.Add(task); limits.Clear();
}
} }
} }
try if (0 < limits.Count)
{
latch.Wait();
}
catch (ThreadInterruptedException)
{ {
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, public RcBuilderResult BuildTile(IInputGeomProvider geom, RcConfig cfg, RcVec3f bmin, RcVec3f bmax, int tx, int ty, RcAtomicInteger progress, int total)
int ty, RcAtomicInteger counter, int total)
{ {
RcBuilderResult result = Build(geom, new RcBuilderConfig(cfg, bmin, bmax, tx, ty)); RcBuilderResult result = Build(geom, new RcBuilderConfig(cfg, bmin, bmax, tx, ty));
if (_progressListener != null) if (_progressListener != null)
{ {
_progressListener.OnProgress(counter.IncrementAndGet(), total); _progressListener.OnProgress(progress.IncrementAndGet(), total);
} }
return result; return result;
@ -205,7 +185,7 @@ namespace DotRecast.Recast
{ {
// Prepare for region partitioning, by calculating distance field along the walkable surface. // Prepare for region partitioning, by calculating distance field along the walkable surface.
RcRegions.BuildDistanceField(ctx, chf); RcRegions.BuildDistanceField(ctx, chf);
// Partition the walkable surface into simple regions without holes. // Partition the walkable surface into simple regions without holes.
RcRegions.BuildRegions(ctx, chf, cfg.MinRegionArea, cfg.MergeRegionArea); RcRegions.BuildRegions(ctx, chf, cfg.MinRegionArea, cfg.MergeRegionArea);
} }
@ -298,7 +278,7 @@ namespace DotRecast.Recast
RcHeightfield solid = RcVoxelizations.BuildSolidHeightfield(ctx, geom, builderCfg); RcHeightfield solid = RcVoxelizations.BuildSolidHeightfield(ctx, geom, builderCfg);
FilterHeightfield(ctx, solid, builderCfg.cfg); FilterHeightfield(ctx, solid, builderCfg.cfg);
RcCompactHeightfield chf = BuildCompactHeightfield(ctx, geom, builderCfg.cfg, solid); RcCompactHeightfield chf = BuildCompactHeightfield(ctx, geom, builderCfg.cfg, solid);
RcLayers.BuildHeightfieldLayers(ctx, chf, builderCfg.cfg.BorderSize, builderCfg.cfg.WalkableHeight, out var lset); RcLayers.BuildHeightfieldLayers(ctx, chf, builderCfg.cfg.BorderSize, builderCfg.cfg.WalkableHeight, out var lset);
return lset; return lset;
} }

View File

@ -78,7 +78,8 @@ public class TestTiledNavMeshBuilder
true, true, true, true, true, true,
SampleAreaModifications.SAMPLE_AREAMOD_GROUND, true); SampleAreaModifications.SAMPLE_AREAMOD_GROUND, true);
RcBuilder rcBuilder = new RcBuilder(); RcBuilder rcBuilder = new RcBuilder();
List<RcBuilderResult> rcResult = rcBuilder.BuildTiles(geom, cfg, null); var task = rcBuilder.BuildTilesAsync(geom, cfg);
List<RcBuilderResult> rcResult = task.Result;
// Add tiles to nav mesh // Add tiles to nav mesh

View File

@ -27,7 +27,6 @@ using NUnit.Framework;
namespace DotRecast.Recast.Test; namespace DotRecast.Recast.Test;
public class RecastTileMeshTest public class RecastTileMeshTest
{ {
private const float m_cellSize = 0.3f; 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) private void Build(IInputGeomProvider geom, RcBuilder builder, RcConfig cfg, int threads, bool validate)
{ {
CancellationTokenSource cts = new CancellationTokenSource(); CancellationTokenSource cts = new CancellationTokenSource();
List<RcBuilderResult> tiles = new(); var task = builder.BuildTilesAsync(geom, cfg, threads, Task.Factory, cts.Token);
var task = builder.BuildTilesAsync(geom, cfg, threads, tiles, Task.Factory, cts.Token); List<RcBuilderResult> tiles = task.Result;
if (validate) if (validate)
{ {
RcBuilderResult rcResult = GetTile(tiles, 7, 8); RcBuilderResult rcResult = GetTile(tiles, 7, 8);