Changed DtNavMeshQuery.GetPolyWallSegments() to use Span<T> for enhanced performance, memory efficiency.

This commit is contained in:
ikpil 2024-07-16 22:19:41 +09:00
parent 84419b1d52
commit cf7aec90ee
5 changed files with 101 additions and 52 deletions

View File

@ -16,6 +16,7 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/).
- Changed data structure of 'neis' from List<byte> to byte[] for optimized memory usage and improved access speed in `DtLayerMonotoneRegion`
- Changed new RcVec3f[3] to stackalloc RcVec3f[3] in DtNavMesh.GetPolyHeight() to reduce heap allocation
- Changed memory handling to use stackalloc in DtNavMeshQuery.GetPolyWallSegments for reducing SOH
- Changed DtNavMeshQuery.GetPolyWallSegments() to use Span<T> for enhanced performance, memory efficiency.
### Removed
- Nothing

View File

@ -18,6 +18,7 @@ freely, subject to the following restrictions:
3. This notice may not be removed or altered from any source distribution.
*/
using System;
using System.Collections.Generic;
using DotRecast.Core;
using DotRecast.Core.Numerics;
@ -90,6 +91,8 @@ namespace DotRecast.Detour.Crowd
public void Update(long startRef, RcVec3f pos, float collisionQueryRange, DtNavMeshQuery navquery, IDtQueryFilter filter)
{
const int MAX_SEGS_PER_POLY = DtDetour.DT_VERTS_PER_POLYGON * 3;
if (startRef == 0)
{
Reset();
@ -104,18 +107,17 @@ namespace DotRecast.Detour.Crowd
{
// Secondly, store all polygon edges.
m_segs.Clear();
var segmentVerts = new List<RcSegmentVert>();
var segmentRefs = new List<long>();
Span<RcSegmentVert> segs = stackalloc RcSegmentVert[MAX_SEGS_PER_POLY];
int nsegs = 0;
for (int j = 0; j < m_polys.Count; ++j)
{
var result = navquery.GetPolyWallSegments(m_polys[j], false, filter, ref segmentVerts, ref segmentRefs);
var result = navquery.GetPolyWallSegments(m_polys[j], filter, segs, null, ref nsegs, MAX_SEGS_PER_POLY);
if (result.Succeeded())
{
for (int k = 0; k < segmentRefs.Count; ++k)
for (int k = 0; k < nsegs; ++k)
{
RcSegmentVert s = segmentVerts[k];
ref RcSegmentVert s = ref segs[k];
var s0 = s.vmin;
var s3 = s.vmax;

View File

@ -3138,11 +3138,11 @@ namespace DotRecast.Detour
/// @param[out] segmentCount The number of segments returned.
/// @param[in] maxSegments The maximum number of segments the result arrays can hold.
/// @returns The status flags for the query.
public DtStatus GetPolyWallSegments(long refs, bool storePortals, IDtQueryFilter filter,
ref List<RcSegmentVert> segmentVerts, ref List<long> segmentRefs)
public DtStatus GetPolyWallSegments(long refs, IDtQueryFilter filter,
Span<RcSegmentVert> segmentVerts, Span<long> segmentRefs, ref int segmentCount,
int maxSegments)
{
segmentVerts.Clear();
segmentRefs.Clear();
segmentCount = 0;
DtStatus status = m_nav.GetTileAndPolyByRef(refs, out var tile, out var poly);
if (status.Failed())
@ -3160,6 +3160,8 @@ namespace DotRecast.Detour
Span<DtSegInterval> ints = stackalloc DtSegInterval[MAX_INTERVAL];
int nints;
bool storePortals = segmentRefs != null;
status = DtStatus.DT_SUCCESS;
for (int i = 0, j = poly.vertCount - 1; i < poly.vertCount; j = i++)
@ -3205,16 +3207,26 @@ namespace DotRecast.Detour
continue;
}
int ivj = poly.verts[j] * 3;
int ivi = poly.verts[i] * 3;
var seg = new RcSegmentVert();
seg.vmin = RcVec.Create(tile.data.verts, ivj);
seg.vmax = RcVec.Create(tile.data.verts, ivi);
// RcArrays.Copy(tile.data.verts, ivj, seg, 0, 3);
// RcArrays.Copy(tile.data.verts, ivi, seg, 3, 3);
segmentVerts.Add(seg);
segmentRefs.Add(neiRef);
n++;
if (n < maxSegments)
{
int ivj = poly.verts[j] * 3;
int ivi = poly.verts[i] * 3;
var seg = new RcSegmentVert();
seg.vmin = RcVec.Create(tile.data.verts, ivj);
seg.vmax = RcVec.Create(tile.data.verts, ivi);
segmentVerts[n] = seg;
if (null != segmentRefs)
{
segmentRefs[n] = neiRef;
}
n++;
}
else
{
status |= DtStatus.DT_BUFFER_TOO_SMALL;
}
continue;
}
@ -3232,12 +3244,25 @@ namespace DotRecast.Detour
{
float tmin = ints[k].tmin / 255.0f;
float tmax = ints[k].tmax / 255.0f;
var seg = new RcSegmentVert();
seg.vmin = RcVec.Lerp(tile.data.verts, vj, vi, tmin);
seg.vmax = RcVec.Lerp(tile.data.verts, vj, vi, tmax);
segmentVerts.Add(seg);
segmentRefs.Add(ints[k].refs);
n++;
if (n < maxSegments)
{
var seg = new RcSegmentVert();
seg.vmin = RcVec.Lerp(tile.data.verts, vj, vi, tmin);
seg.vmax = RcVec.Lerp(tile.data.verts, vj, vi, tmax);
segmentVerts[n] = seg;
if (null != segmentRefs)
{
segmentRefs[n] = ints[k].refs;
}
n++;
}
else
{
status |= DtStatus.DT_BUFFER_TOO_SMALL;
}
}
// Wall segment.
@ -3247,16 +3272,31 @@ namespace DotRecast.Detour
{
float tmin = imin / 255.0f;
float tmax = imax / 255.0f;
var seg = new RcSegmentVert();
seg.vmin = RcVec.Lerp(tile.data.verts, vj, vi, tmin);
seg.vmax = RcVec.Lerp(tile.data.verts, vj, vi, tmax);
segmentVerts.Add(seg);
segmentRefs.Add(0L);
n++;
if (n < maxSegments)
{
var seg = new RcSegmentVert();
seg.vmin = RcVec.Lerp(tile.data.verts, vj, vi, tmin);
seg.vmax = RcVec.Lerp(tile.data.verts, vj, vi, tmax);
segmentVerts[n] = seg;
if (null != segmentRefs)
{
segmentRefs[n] = 0;
}
n++;
}
else
{
status |= DtStatus.DT_BUFFER_TOO_SMALL;
}
}
}
}
segmentCount = n;
return status;
}

View File

@ -472,8 +472,9 @@ public class TestNavmeshSampleTool : ISampleTool
{
if (m_polys != null)
{
var segmentVerts = new List<RcSegmentVert>();
var segmentRefs = new List<long>();
const int MAX_SEGS = DtDetour.DT_VERTS_PER_POLYGON * 4;
Span<RcSegmentVert> segs = stackalloc RcSegmentVert[MAX_SEGS];
Span<long> refs = stackalloc long[MAX_SEGS];
for (int i = 0; i < m_polys.Count; i++)
{
@ -491,18 +492,20 @@ public class TestNavmeshSampleTool : ISampleTool
dd.DepthMask(true);
if (_sample.GetNavMeshQuery() != null)
{
int nsegs = 0;
var result = _sample
.GetNavMeshQuery()
.GetPolyWallSegments(m_polys[i], false, m_filter, ref segmentVerts, ref segmentRefs);
.GetPolyWallSegments(m_polys[i], m_filter, segs, refs, ref nsegs, MAX_SEGS);
if (result.Succeeded())
{
dd.Begin(LINES, 2.0f);
for (int j = 0; j < segmentVerts.Count; ++j)
for (int j = 0; j < nsegs; ++j)
{
RcSegmentVert s = segmentVerts[j];
ref RcSegmentVert s = ref segs[j];
var v0 = s.vmin;
var s3 = s.vmax;
// Skip too distant segments.
var distSqr = DtUtils.DistancePtSegSqr2D(m_spos, v0, s3, out var tseg);
if (distSqr > RcMath.Sqr(m_neighbourhoodRadius))
@ -515,8 +518,9 @@ public class TestNavmeshSampleTool : ISampleTool
RcVec3f norm = new RcVec3f(delta.Z, 0, -delta.X);
norm = RcVec3f.Normalize(norm);
RcVec3f p1 = RcVec.Mad(p0, norm, agentRadius * 0.5f);
// Skip backfacing segments.
if (segmentRefs[j] != 0)
if (refs[j] != 0)
{
int col = DuRGBA(255, 255, 255, 32);
dd.Vertex(s.vmin.X, s.vmin.Y + agentClimb, s.vmin.Z, col);

View File

@ -17,13 +17,13 @@ freely, subject to the following restrictions:
3. This notice may not be removed or altered from any source distribution.
*/
using System;
using System.Collections.Generic;
using DotRecast.Core;
using NUnit.Framework;
namespace DotRecast.Detour.Test;
public class GetPolyWallSegmentsTest : AbstractDetourTest
{
private static readonly RcSegmentVert[][] VERTICES =
@ -83,28 +83,30 @@ public class GetPolyWallSegmentsTest : AbstractDetourTest
[Test]
public void TestFindDistanceToWall()
{
var segmentVerts = new List<RcSegmentVert>();
var segmentRefs = new List<long>();
const int MAX_SEGS = DtDetour.DT_VERTS_PER_POLYGON * 4;
Span<RcSegmentVert> segs = stackalloc RcSegmentVert[MAX_SEGS];
Span<long> refs = stackalloc long[MAX_SEGS];
int nsegs = 0;
IDtQueryFilter filter = new DtQueryDefaultFilter();
for (int i = 0; i < startRefs.Length; i++)
{
var result = query.GetPolyWallSegments(startRefs[i], true, filter, ref segmentVerts, ref segmentRefs);
Assert.That(segmentVerts.Count, Is.EqualTo(VERTICES[i].Length));
Assert.That(segmentRefs.Count, Is.EqualTo(REFS[i].Length));
var result = query.GetPolyWallSegments(startRefs[i], filter, segs, refs, ref nsegs, MAX_SEGS);
Assert.That(nsegs, Is.EqualTo(VERTICES[i].Length));
Assert.That(nsegs, Is.EqualTo(REFS[i].Length));
for (int v = 0; v < VERTICES[i].Length / 6; v++)
{
Assert.That(segmentVerts[v].vmin.X, Is.EqualTo(VERTICES[i][v].vmin.X).Within(0.001f));
Assert.That(segmentVerts[v].vmin.Y, Is.EqualTo(VERTICES[i][v].vmin.Y).Within(0.001f));
Assert.That(segmentVerts[v].vmin.Z, Is.EqualTo(VERTICES[i][v].vmin.Z).Within(0.001f));
Assert.That(segmentVerts[v].vmax.X, Is.EqualTo(VERTICES[i][v].vmax.X).Within(0.001f));
Assert.That(segmentVerts[v].vmax.Y, Is.EqualTo(VERTICES[i][v].vmax.Y).Within(0.001f));
Assert.That(segmentVerts[v].vmax.Z, Is.EqualTo(VERTICES[i][v].vmax.Z).Within(0.001f));
Assert.That(segs[v].vmin.X, Is.EqualTo(VERTICES[i][v].vmin.X).Within(0.001f));
Assert.That(segs[v].vmin.Y, Is.EqualTo(VERTICES[i][v].vmin.Y).Within(0.001f));
Assert.That(segs[v].vmin.Z, Is.EqualTo(VERTICES[i][v].vmin.Z).Within(0.001f));
Assert.That(segs[v].vmax.X, Is.EqualTo(VERTICES[i][v].vmax.X).Within(0.001f));
Assert.That(segs[v].vmax.Y, Is.EqualTo(VERTICES[i][v].vmax.Y).Within(0.001f));
Assert.That(segs[v].vmax.Z, Is.EqualTo(VERTICES[i][v].vmax.Z).Within(0.001f));
}
for (int v = 0; v < REFS[i].Length; v++)
{
Assert.That(segmentRefs[v], Is.EqualTo(REFS[i][v]));
Assert.That(refs[v], Is.EqualTo(REFS[i][v]));
}
}
}