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 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 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 memory handling to use stackalloc in DtNavMeshQuery.GetPolyWallSegments for reducing SOH
- Changed DtNavMeshQuery.GetPolyWallSegments() to use Span<T> for enhanced performance, memory efficiency.
### Removed ### Removed
- Nothing - 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. 3. This notice may not be removed or altered from any source distribution.
*/ */
using System;
using System.Collections.Generic; using System.Collections.Generic;
using DotRecast.Core; using DotRecast.Core;
using DotRecast.Core.Numerics; 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) 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) if (startRef == 0)
{ {
Reset(); Reset();
@ -104,18 +107,17 @@ namespace DotRecast.Detour.Crowd
{ {
// Secondly, store all polygon edges. // Secondly, store all polygon edges.
m_segs.Clear(); m_segs.Clear();
Span<RcSegmentVert> segs = stackalloc RcSegmentVert[MAX_SEGS_PER_POLY];
var segmentVerts = new List<RcSegmentVert>(); int nsegs = 0;
var segmentRefs = new List<long>();
for (int j = 0; j < m_polys.Count; ++j) 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()) 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 s0 = s.vmin;
var s3 = s.vmax; var s3 = s.vmax;

View File

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

View File

@ -472,8 +472,9 @@ public class TestNavmeshSampleTool : ISampleTool
{ {
if (m_polys != null) if (m_polys != null)
{ {
var segmentVerts = new List<RcSegmentVert>(); const int MAX_SEGS = DtDetour.DT_VERTS_PER_POLYGON * 4;
var segmentRefs = new List<long>(); Span<RcSegmentVert> segs = stackalloc RcSegmentVert[MAX_SEGS];
Span<long> refs = stackalloc long[MAX_SEGS];
for (int i = 0; i < m_polys.Count; i++) for (int i = 0; i < m_polys.Count; i++)
{ {
@ -491,18 +492,20 @@ public class TestNavmeshSampleTool : ISampleTool
dd.DepthMask(true); dd.DepthMask(true);
if (_sample.GetNavMeshQuery() != null) if (_sample.GetNavMeshQuery() != null)
{ {
int nsegs = 0;
var result = _sample var result = _sample
.GetNavMeshQuery() .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()) if (result.Succeeded())
{ {
dd.Begin(LINES, 2.0f); 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 v0 = s.vmin;
var s3 = s.vmax; var s3 = s.vmax;
// Skip too distant segments. // Skip too distant segments.
var distSqr = DtUtils.DistancePtSegSqr2D(m_spos, v0, s3, out var tseg); var distSqr = DtUtils.DistancePtSegSqr2D(m_spos, v0, s3, out var tseg);
if (distSqr > RcMath.Sqr(m_neighbourhoodRadius)) if (distSqr > RcMath.Sqr(m_neighbourhoodRadius))
@ -515,8 +518,9 @@ public class TestNavmeshSampleTool : ISampleTool
RcVec3f norm = new RcVec3f(delta.Z, 0, -delta.X); RcVec3f norm = new RcVec3f(delta.Z, 0, -delta.X);
norm = RcVec3f.Normalize(norm); norm = RcVec3f.Normalize(norm);
RcVec3f p1 = RcVec.Mad(p0, norm, agentRadius * 0.5f); RcVec3f p1 = RcVec.Mad(p0, norm, agentRadius * 0.5f);
// Skip backfacing segments. // Skip backfacing segments.
if (segmentRefs[j] != 0) if (refs[j] != 0)
{ {
int col = DuRGBA(255, 255, 255, 32); int col = DuRGBA(255, 255, 255, 32);
dd.Vertex(s.vmin.X, s.vmin.Y + agentClimb, s.vmin.Z, col); 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. 3. This notice may not be removed or altered from any source distribution.
*/ */
using System;
using System.Collections.Generic; using System.Collections.Generic;
using DotRecast.Core; using DotRecast.Core;
using NUnit.Framework; using NUnit.Framework;
namespace DotRecast.Detour.Test; namespace DotRecast.Detour.Test;
public class GetPolyWallSegmentsTest : AbstractDetourTest public class GetPolyWallSegmentsTest : AbstractDetourTest
{ {
private static readonly RcSegmentVert[][] VERTICES = private static readonly RcSegmentVert[][] VERTICES =
@ -83,28 +83,30 @@ public class GetPolyWallSegmentsTest : AbstractDetourTest
[Test] [Test]
public void TestFindDistanceToWall() public void TestFindDistanceToWall()
{ {
var segmentVerts = new List<RcSegmentVert>(); const int MAX_SEGS = DtDetour.DT_VERTS_PER_POLYGON * 4;
var segmentRefs = new List<long>(); Span<RcSegmentVert> segs = stackalloc RcSegmentVert[MAX_SEGS];
Span<long> refs = stackalloc long[MAX_SEGS];
int nsegs = 0;
IDtQueryFilter filter = new DtQueryDefaultFilter(); IDtQueryFilter filter = new DtQueryDefaultFilter();
for (int i = 0; i < startRefs.Length; i++) for (int i = 0; i < startRefs.Length; i++)
{ {
var result = query.GetPolyWallSegments(startRefs[i], true, filter, ref segmentVerts, ref segmentRefs); var result = query.GetPolyWallSegments(startRefs[i], filter, segs, refs, ref nsegs, MAX_SEGS);
Assert.That(segmentVerts.Count, Is.EqualTo(VERTICES[i].Length)); Assert.That(nsegs, Is.EqualTo(VERTICES[i].Length));
Assert.That(segmentRefs.Count, Is.EqualTo(REFS[i].Length)); Assert.That(nsegs, Is.EqualTo(REFS[i].Length));
for (int v = 0; v < VERTICES[i].Length / 6; v++) 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(segs[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(segs[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(segs[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(segs[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(segs[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].vmax.Z, Is.EqualTo(VERTICES[i][v].vmax.Z).Within(0.001f));
} }
for (int v = 0; v < REFS[i].Length; v++) 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]));
} }
} }
} }