Compare commits

..

4 Commits

Author SHA1 Message Date
ikpil b81a5923a3 benchmark arrays 2024-02-24 20:36:33 +09:00
ikpil 3fbfb968d0 test RcRentedArray 2024-02-24 20:00:51 +09:00
ikpil 3c4723c907 added npath in MergeCorridor functions 2024-02-23 01:02:23 +09:00
ikpil 7836b78bb4 added DtCrowdConst.MAX_PATH_RESULT = 256 2024-02-23 00:52:23 +09:00
11 changed files with 135 additions and 37 deletions

View File

@ -6,7 +6,7 @@ namespace DotRecast.Core.Buffers
{ {
public static class RcRentedArray public static class RcRentedArray
{ {
public static RcRentedArray<T> RentDisposableArray<T>(int minimumLength) public static RcRentedArray<T> Rent<T>(int minimumLength)
{ {
var array = ArrayPool<T>.Shared.Rent(minimumLength); var array = ArrayPool<T>.Shared.Rent(minimumLength);
return new RcRentedArray<T>(ArrayPool<T>.Shared, array, minimumLength); return new RcRentedArray<T>(ArrayPool<T>.Shared, array, minimumLength);
@ -17,44 +17,41 @@ namespace DotRecast.Core.Buffers
{ {
private ArrayPool<T> _owner; private ArrayPool<T> _owner;
private T[] _array; private T[] _array;
private readonly RcAtomicInteger _disposed;
public int Length { get; } public int Length { get; }
public bool IsDisposed => null == _owner || null == _array;
internal RcRentedArray(ArrayPool<T> owner, T[] array, int length) internal RcRentedArray(ArrayPool<T> owner, T[] array, int length)
{ {
_owner = owner; _owner = owner;
_array = array; _array = array;
Length = length; Length = length;
_disposed = new RcAtomicInteger(0);
} }
public T this[int index] public ref T this[int index]
{ {
[MethodImpl(MethodImplOptions.AggressiveInlining)] [MethodImpl(MethodImplOptions.AggressiveInlining)]
get get
{ {
RcThrowHelper.ThrowExceptionIfIndexOutOfRange(index, Length); RcThrowHelper.ThrowExceptionIfIndexOutOfRange(index, Length);
return _array[index]; return ref _array[index];
} }
}
[MethodImpl(MethodImplOptions.AggressiveInlining)] public T[] AsArray()
set {
{ return _array;
RcThrowHelper.ThrowExceptionIfIndexOutOfRange(index, Length);
_array[index] = value;
}
} }
public void Dispose() public void Dispose()
{ {
if (1 != _disposed.IncrementAndGet()) if (null != _owner && null != _array)
return; {
_owner.Return(_array, true);
_owner?.Return(_array, true); _owner = null;
_array = null; _array = null;
_owner = null; }
} }
} }
} }

View File

@ -168,7 +168,7 @@ namespace DotRecast.Detour.Crowd
} }
// Allocate temp buffer for merging paths. // Allocate temp buffer for merging paths.
_maxPathResult = 256; _maxPathResult = DtCrowdConst.MAX_PATH_RESULT;
_pathQ = new DtPathQueue(config); _pathQ = new DtPathQueue(config);
_agents = new List<DtCrowdAgent>(); _agents = new List<DtCrowdAgent>();

View File

@ -30,5 +30,6 @@
public const int MAX_ITERS_PER_UPDATE = 100; public const int MAX_ITERS_PER_UPDATE = 100;
public const int MAX_PATHQUEUE_NODES = 4096; public const int MAX_PATHQUEUE_NODES = 4096;
public const int MAX_COMMON_NODES = 512; public const int MAX_COMMON_NODES = 512;
public const int MAX_PATH_RESULT = 256;
} }
} }

View File

@ -215,7 +215,7 @@ namespace DotRecast.Detour.Crowd
{ {
if (res.Count > 1 && t > 0.99f) if (res.Count > 1 && t > 0.99f)
{ {
m_path = DtPathUtils.MergeCorridorStartShortcut(m_path, m_maxPath, res); m_path = DtPathUtils.MergeCorridorStartShortcut(m_path, m_path.Count, m_maxPath, res);
} }
} }
} }
@ -241,13 +241,13 @@ namespace DotRecast.Detour.Crowd
} }
var res = new List<long>(); var res = new List<long>();
navquery.InitSlicedFindPath(m_path[0], m_path[m_path.Count - 1], m_pos, m_target, filter, 0); navquery.InitSlicedFindPath(m_path[0], m_path[^1], m_pos, m_target, filter, 0);
navquery.UpdateSlicedFindPath(maxIterations, out var _); navquery.UpdateSlicedFindPath(maxIterations, out var _);
var status = navquery.FinalizeSlicedFindPathPartial(m_path, ref res); var status = navquery.FinalizeSlicedFindPathPartial(m_path, ref res);
if (status.Succeeded() && res.Count > 0) if (status.Succeeded() && res.Count > 0)
{ {
m_path = DtPathUtils.MergeCorridorStartShortcut(m_path, m_maxPath, res); m_path = DtPathUtils.MergeCorridorStartShortcut(m_path, m_path.Count, m_maxPath, res);
return true; return true;
} }
@ -316,7 +316,7 @@ namespace DotRecast.Detour.Crowd
var status = navquery.MoveAlongSurface(m_path[0], m_pos, npos, filter, out var result, ref visited); var status = navquery.MoveAlongSurface(m_path[0], m_pos, npos, filter, out var result, ref visited);
if (status.Succeeded()) if (status.Succeeded())
{ {
m_path = DtPathUtils.MergeCorridorStartMoved(m_path, m_maxPath, visited); m_path = DtPathUtils.MergeCorridorStartMoved(m_path, m_path.Count, m_maxPath, visited);
// Adjust the position to stay on top of the navmesh. // Adjust the position to stay on top of the navmesh.
m_pos = result; m_pos = result;
@ -355,10 +355,10 @@ namespace DotRecast.Detour.Crowd
{ {
// Move along navmesh and update new position. // Move along navmesh and update new position.
var visited = new List<long>(); var visited = new List<long>();
var status = navquery.MoveAlongSurface(m_path[m_path.Count - 1], m_target, npos, filter, out var result, ref visited); var status = navquery.MoveAlongSurface(m_path[^1], m_target, npos, filter, out var result, ref visited);
if (status.Succeeded()) if (status.Succeeded())
{ {
m_path = DtPathUtils.MergeCorridorEndMoved(m_path, m_maxPath, visited); m_path = DtPathUtils.MergeCorridorEndMoved(m_path, m_path.Count, m_maxPath, visited);
// TODO: should we do that? // TODO: should we do that?
// Adjust the position to stay on top of the navmesh. // Adjust the position to stay on top of the navmesh.
/* /*

View File

@ -358,8 +358,8 @@ namespace DotRecast.Detour
var tbmax = tile.data.header.bmax; var tbmax = tile.data.header.bmax;
float qfac = tile.data.header.bvQuantFactor; float qfac = tile.data.header.bvQuantFactor;
// Calculate quantized box // Calculate quantized box
Span<int> bmin = stackalloc int[3]; int[] bmin = new int[3];
Span<int> bmax = stackalloc int[3]; int[] bmax = new int[3];
// dtClamp query box to world box. // dtClamp query box to world box.
float minx = Math.Clamp(qmin.X, tbmin.X, tbmax.X) - tbmin.X; float minx = Math.Clamp(qmin.X, tbmin.X, tbmax.X) - tbmin.X;
float miny = Math.Clamp(qmin.Y, tbmin.Y, tbmax.Y) - tbmin.Y; float miny = Math.Clamp(qmin.Y, tbmin.Y, tbmax.Y) - tbmin.Y;

View File

@ -583,8 +583,8 @@ namespace DotRecast.Detour
var tbmax = tile.data.header.bmax; var tbmax = tile.data.header.bmax;
float qfac = tile.data.header.bvQuantFactor; float qfac = tile.data.header.bvQuantFactor;
// Calculate quantized box // Calculate quantized box
Span<int> bmin = stackalloc int[3]; int[] bmin = new int[3];
Span<int> bmax = stackalloc int[3]; int[] bmax = new int[3];
// dtClamp query box to world box. // dtClamp query box to world box.
float minx = Math.Clamp(qmin.X, tbmin.X, tbmax.X) - tbmin.X; float minx = Math.Clamp(qmin.X, tbmin.X, tbmax.X) - tbmin.X;
float miny = Math.Clamp(qmin.Y, tbmin.Y, tbmax.Y) - tbmin.Y; float miny = Math.Clamp(qmin.Y, tbmin.Y, tbmax.Y) - tbmin.Y;
@ -1824,9 +1824,6 @@ namespace DotRecast.Detour
float[] verts = new float[m_nav.GetMaxVertsPerPoly() * 3]; float[] verts = new float[m_nav.GetMaxVertsPerPoly() * 3];
const int MAX_NEIS = 8;
Span<long> neis = stackalloc long[MAX_NEIS];
while (0 < stack.Count) while (0 < stack.Count)
{ {
// Pop front. // Pop front.
@ -1857,7 +1854,9 @@ namespace DotRecast.Detour
for (int i = 0, j = curPoly.vertCount - 1; i < curPoly.vertCount; j = i++) for (int i = 0, j = curPoly.vertCount - 1; i < curPoly.vertCount; j = i++)
{ {
// Find links to neighbours. // Find links to neighbours.
int MAX_NEIS = 8;
int nneis = 0; int nneis = 0;
long[] neis = new long[MAX_NEIS];
if ((curPoly.neis[j] & DtNavMesh.DT_EXT_LINK) != 0) if ((curPoly.neis[j] & DtNavMesh.DT_EXT_LINK) != 0)
{ {

View File

@ -141,7 +141,7 @@ namespace DotRecast.Detour
return path; return path;
} }
public static List<long> MergeCorridorStartMoved(List<long> path, int maxPath, List<long> visited) public static List<long> MergeCorridorStartMoved(List<long> path, int npath, int maxPath, List<long> visited)
{ {
int furthestPath = -1; int furthestPath = -1;
int furthestVisited = -1; int furthestVisited = -1;
@ -186,7 +186,7 @@ namespace DotRecast.Detour
return result; return result;
} }
public static List<long> MergeCorridorEndMoved(List<long> path, int maxPath, List<long> visited) public static List<long> MergeCorridorEndMoved(List<long> path, int npath, int maxPath, List<long> visited)
{ {
int furthestPath = -1; int furthestPath = -1;
int furthestVisited = -1; int furthestVisited = -1;
@ -223,7 +223,7 @@ namespace DotRecast.Detour
return result; return result;
} }
public static List<long> MergeCorridorStartShortcut(List<long> path, int maxPath, List<long> visited) public static List<long> MergeCorridorStartShortcut(List<long> path, int npath, int maxPath, List<long> visited)
{ {
int furthestPath = -1; int furthestPath = -1;
int furthestVisited = -1; int furthestVisited = -1;

View File

@ -64,7 +64,7 @@ namespace DotRecast.Detour
/// @param[in] bmax Maximum bounds of box B. [(x, y, z)] /// @param[in] bmax Maximum bounds of box B. [(x, y, z)]
/// @return True if the two AABB's overlap. /// @return True if the two AABB's overlap.
/// @see dtOverlapBounds /// @see dtOverlapBounds
public static bool OverlapQuantBounds(Span<int> amin, Span<int> amax, Span<int> bmin, Span<int> bmax) public static bool OverlapQuantBounds(int[] amin, int[] amax, int[] bmin, int[] bmax)
{ {
bool overlap = true; bool overlap = true;
overlap = (amin[0] > bmax[0] || amax[0] < bmin[0]) ? false : overlap; overlap = (amin[0] > bmax[0] || amax[0] < bmin[0]) ? false : overlap;

View File

@ -92,7 +92,7 @@ namespace DotRecast.Recast.Toolset.Tools
iterPos = result; iterPos = result;
pathIterPolys = DtPathUtils.MergeCorridorStartMoved(pathIterPolys, MAX_POLYS, visited); pathIterPolys = DtPathUtils.MergeCorridorStartMoved(pathIterPolys, pathIterPolys.Count, MAX_POLYS, visited);
pathIterPolys = DtPathUtils.FixupShortcuts(pathIterPolys, navQuery); pathIterPolys = DtPathUtils.FixupShortcuts(pathIterPolys, navQuery);
var status = navQuery.GetPolyHeight(pathIterPolys[0], result, out var h); var status = navQuery.GetPolyHeight(pathIterPolys[0], result, out var h);

View File

@ -0,0 +1,101 @@
using System;
using System.Buffers;
using System.Collections.Generic;
using DotRecast.Core.Buffers;
using DotRecast.Core.Collections;
using NUnit.Framework;
namespace DotRecast.Core.Test;
public class RcArrayBenchmarkTests
{
private const int StepLength = 512;
private const int RandomLoop = 1000;
private readonly RcRand _rand = new RcRand();
private (string title, long ticks) Bench(string title, Action<int> source)
{
var begin = RcFrequency.Ticks;
for (int step = StepLength; step > 0; --step)
{
for (int i = 0; i < RandomLoop; ++i)
{
source.Invoke(step);
}
}
var end = RcFrequency.Ticks - begin;
return (title, end);
}
private void RoundForArray(int len)
{
var array = new int[len];
for (int ii = 0; ii < len; ++ii)
{
array[ii] = _rand.NextInt32();
}
}
private void RoundForPureRentArray(int len)
{
var array = ArrayPool<int>.Shared.Rent(len);
for (int ii = 0; ii < array.Length; ++ii)
{
array[ii] = _rand.NextInt32();
}
ArrayPool<int>.Shared.Return(array);
}
private void RoundForRcRentedArray(int len)
{
using var rentedArray = RcRentedArray.Rent<int>(len);
var array = rentedArray.AsArray();
for (int i = 0; i < rentedArray.Length; ++i)
{
array[i] = _rand.NextInt32();
}
}
private void RoundForRcStackArray(int len)
{
var array = new RcStackArray512<int>();
for (int ii = 0; ii < len; ++ii)
{
array[ii] = _rand.NextInt32();
}
}
private void RoundForStackalloc(int len)
{
Span<int> array = stackalloc int[len];
for (int ii = 0; ii < len; ++ii)
{
array[ii] = _rand.NextInt32();
}
}
[Test]
public void TestBenchmarkArrays()
{
var list = new List<(string title, long ticks)>();
list.Add(Bench("new int[len]", RoundForArray));
list.Add(Bench("ArrayPool<int>.Shared.Rent(len)", RoundForPureRentArray));
list.Add(Bench("RcRentedArray.Rent<int>(len)", RoundForRcRentedArray));
list.Add(Bench("new RcStackArray512<int>()", RoundForRcStackArray));
list.Add(Bench("stackalloc int[len]", RoundForStackalloc));
list.Sort((x, y) => x.ticks.CompareTo(y.ticks));
foreach (var t in list)
{
Console.WriteLine($"{t.title} {t.ticks / (double)TimeSpan.TicksPerMillisecond} ms");
}
}
}

View File

@ -31,7 +31,7 @@ public class RcRentedArrayTest
{ {
int length = Math.Max(2, (int)(rand.Next() * 2048)); int length = Math.Max(2, (int)(rand.Next() * 2048));
var values = RandomValues(length); var values = RandomValues(length);
using var array = RcRentedArray.RentDisposableArray<int>(length); using var array = RcRentedArray.Rent<int>(length);
for (int i = 0; i < array.Length; ++i) for (int i = 0; i < array.Length; ++i)
{ {