forked from mirror/DotRecast
Compare commits
6 Commits
master
...
risky_opti
Author | SHA1 | Date |
---|---|---|
wrenge | 96ffed87e3 | |
wrenge | 1315de063f | |
wrenge | a6db4344e4 | |
wrenge | a7b9b772c4 | |
wrenge | 7aeb31d369 | |
wrenge | 18b544845f |
|
@ -1,31 +1,96 @@
|
||||||
using System;
|
using System;
|
||||||
using System.Buffers;
|
using System.Buffers;
|
||||||
|
using System.Collections.Generic;
|
||||||
using System.Runtime.CompilerServices;
|
using System.Runtime.CompilerServices;
|
||||||
|
using System.Threading;
|
||||||
|
|
||||||
namespace DotRecast.Core.Buffers
|
namespace DotRecast.Core.Buffers
|
||||||
{
|
{
|
||||||
public static class RcRentedArray
|
public static class RcRentedArray
|
||||||
{
|
{
|
||||||
|
private sealed class RentIdPool
|
||||||
|
{
|
||||||
|
private int[] _generations;
|
||||||
|
private readonly Queue<int> _freeIds;
|
||||||
|
private int _maxId;
|
||||||
|
|
||||||
|
public RentIdPool(int capacity)
|
||||||
|
{
|
||||||
|
_generations = new int[capacity];
|
||||||
|
_freeIds = new Queue<int>(capacity);
|
||||||
|
}
|
||||||
|
|
||||||
|
internal RentIdGen AcquireId()
|
||||||
|
{
|
||||||
|
if (!_freeIds.TryDequeue(out int id))
|
||||||
|
{
|
||||||
|
id = _maxId++;
|
||||||
|
if(_generations.Length <= id)
|
||||||
|
Array.Resize(ref _generations, _generations.Length << 1);
|
||||||
|
}
|
||||||
|
|
||||||
|
return new RentIdGen(id, _generations[id]);
|
||||||
|
}
|
||||||
|
|
||||||
|
internal void ReturnId(int id)
|
||||||
|
{
|
||||||
|
_generations[id]++;
|
||||||
|
_freeIds.Enqueue(id);
|
||||||
|
}
|
||||||
|
|
||||||
|
internal int GetGeneration(int id)
|
||||||
|
{
|
||||||
|
return _generations.Length <= id ? 0 : _generations[id];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public const int START_RENT_ID_POOL_CAPACITY = 16;
|
||||||
|
private static readonly ThreadLocal<RentIdPool> _rentPool = new ThreadLocal<RentIdPool>(() => new RentIdPool(START_RENT_ID_POOL_CAPACITY));
|
||||||
|
|
||||||
public static RcRentedArray<T> Rent<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, _rentPool.Value.AcquireId(), array, minimumLength);
|
||||||
|
}
|
||||||
|
|
||||||
|
internal static bool IsDisposed(RentIdGen rentIdGen)
|
||||||
|
{
|
||||||
|
return _rentPool.Value.GetGeneration(rentIdGen.Id) != rentIdGen.Gen;
|
||||||
|
}
|
||||||
|
|
||||||
|
internal static void ReturnId(RentIdGen rentIdGen)
|
||||||
|
{
|
||||||
|
_rentPool.Value.ReturnId(rentIdGen.Id);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public class RcRentedArray<T> : IDisposable
|
public readonly struct RentIdGen
|
||||||
|
{
|
||||||
|
public readonly int Id;
|
||||||
|
public readonly int Gen;
|
||||||
|
|
||||||
|
public RentIdGen(int id, int gen)
|
||||||
|
{
|
||||||
|
Id = id;
|
||||||
|
Gen = gen;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public struct RcRentedArray<T> : IDisposable
|
||||||
{
|
{
|
||||||
private ArrayPool<T> _owner;
|
private ArrayPool<T> _owner;
|
||||||
private T[] _array;
|
private T[] _array;
|
||||||
|
private readonly RentIdGen _rentIdGen;
|
||||||
|
|
||||||
public int Length { get; }
|
public int Length { get; }
|
||||||
public bool IsDisposed => null == _owner || null == _array;
|
public bool IsDisposed => null == _owner || null == _array || RcRentedArray.IsDisposed(_rentIdGen);
|
||||||
|
|
||||||
internal RcRentedArray(ArrayPool<T> owner, T[] array, int length)
|
internal RcRentedArray(ArrayPool<T> owner, RentIdGen rentIdGen, T[] array, int length)
|
||||||
{
|
{
|
||||||
_owner = owner;
|
_owner = owner;
|
||||||
_array = array;
|
_array = array;
|
||||||
Length = length;
|
Length = length;
|
||||||
|
_rentIdGen = rentIdGen;
|
||||||
}
|
}
|
||||||
|
|
||||||
public ref T this[int index]
|
public ref T this[int index]
|
||||||
|
@ -34,6 +99,8 @@ namespace DotRecast.Core.Buffers
|
||||||
get
|
get
|
||||||
{
|
{
|
||||||
RcThrowHelper.ThrowExceptionIfIndexOutOfRange(index, Length);
|
RcThrowHelper.ThrowExceptionIfIndexOutOfRange(index, Length);
|
||||||
|
if (IsDisposed)
|
||||||
|
throw new NullReferenceException();
|
||||||
return ref _array[index];
|
return ref _array[index];
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -51,12 +118,14 @@ namespace DotRecast.Core.Buffers
|
||||||
|
|
||||||
public void Dispose()
|
public void Dispose()
|
||||||
{
|
{
|
||||||
if (null != _owner && null != _array)
|
if (null != _owner && null != _array && !RcRentedArray.IsDisposed(_rentIdGen))
|
||||||
{
|
{
|
||||||
|
RcRentedArray.ReturnId(_rentIdGen);
|
||||||
_owner.Return(_array, true);
|
_owner.Return(_array, true);
|
||||||
_owner = null;
|
|
||||||
_array = null;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
_owner = null;
|
||||||
|
_array = null;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
|
@ -27,12 +27,12 @@ namespace DotRecast.Core.Collections
|
||||||
{
|
{
|
||||||
private bool _dirty;
|
private bool _dirty;
|
||||||
private readonly List<T> _items;
|
private readonly List<T> _items;
|
||||||
private readonly Comparer<T> _comparer;
|
private readonly Comparison<T> _comparison;
|
||||||
|
|
||||||
public RcSortedQueue(Comparison<T> comp)
|
public RcSortedQueue(Comparison<T> comp)
|
||||||
{
|
{
|
||||||
_items = new List<T>();
|
_items = new List<T>();
|
||||||
_comparer = Comparer<T>.Create((x, y) => comp.Invoke(x, y) * -1);
|
_comparison = (x, y) => comp(x, y) * -1;
|
||||||
}
|
}
|
||||||
|
|
||||||
public int Count()
|
public int Count()
|
||||||
|
@ -55,7 +55,7 @@ namespace DotRecast.Core.Collections
|
||||||
{
|
{
|
||||||
if (_dirty)
|
if (_dirty)
|
||||||
{
|
{
|
||||||
_items.Sort(_comparer); // reverse
|
_items.Sort(_comparison); // reverse
|
||||||
_dirty = false;
|
_dirty = false;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -52,25 +52,12 @@ namespace DotRecast.Detour.Extras.Jumplink
|
||||||
|
|
||||||
RcVec3f halfExtents = new RcVec3f { X = cs, Y = heightRange, Z = cs };
|
RcVec3f halfExtents = new RcVec3f { X = cs, Y = heightRange, Z = cs };
|
||||||
float maxHeight = pt.Y + heightRange;
|
float maxHeight = pt.Y + heightRange;
|
||||||
RcAtomicBoolean found = new RcAtomicBoolean();
|
var query = new DtHeightSamplePolyQuery(navMeshQuery, pt, pt.Y, maxHeight);
|
||||||
RcAtomicFloat minHeight = new RcAtomicFloat(pt.Y);
|
navMeshQuery.QueryPolygons(pt, halfExtents, DtQueryNoOpFilter.Shared, ref query);
|
||||||
|
|
||||||
navMeshQuery.QueryPolygons(pt, halfExtents, DtQueryNoOpFilter.Shared, new DtCallbackPolyQuery((tile, poly, refs) =>
|
if (query.Found)
|
||||||
{
|
{
|
||||||
var status = navMeshQuery.GetPolyHeight(refs, pt, out var h);
|
height = query.MinHeight;
|
||||||
if (status.Succeeded())
|
|
||||||
{
|
|
||||||
if (h > minHeight.Get() && h < maxHeight)
|
|
||||||
{
|
|
||||||
minHeight.Exchange(h);
|
|
||||||
found.Set(true);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}));
|
|
||||||
|
|
||||||
if (found.Get())
|
|
||||||
{
|
|
||||||
height = minHeight.Get();
|
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -2,7 +2,7 @@ using System;
|
||||||
|
|
||||||
namespace DotRecast.Detour
|
namespace DotRecast.Detour
|
||||||
{
|
{
|
||||||
public class DtCallbackPolyQuery : IDtPolyQuery
|
public struct DtCallbackPolyQuery : IDtPolyQuery
|
||||||
{
|
{
|
||||||
private readonly Action<DtMeshTile, DtPoly, long> _callback;
|
private readonly Action<DtMeshTile, DtPoly, long> _callback;
|
||||||
|
|
||||||
|
@ -11,7 +11,7 @@ namespace DotRecast.Detour
|
||||||
_callback = callback;
|
_callback = callback;
|
||||||
}
|
}
|
||||||
|
|
||||||
public void Process(DtMeshTile tile, DtPoly[] poly, Span<long> refs, int count)
|
public void Process(DtMeshTile tile, Span<DtPoly> poly, Span<long> refs, int count)
|
||||||
{
|
{
|
||||||
for (int i = 0; i < count; ++i)
|
for (int i = 0; i < count; ++i)
|
||||||
{
|
{
|
||||||
|
|
|
@ -26,7 +26,7 @@ namespace DotRecast.Detour
|
||||||
return m_overflow;
|
return m_overflow;
|
||||||
}
|
}
|
||||||
|
|
||||||
public void Process(DtMeshTile tile, DtPoly[] poly, Span<long> refs, int count)
|
public void Process(DtMeshTile tile, Span<DtPoly> poly, Span<long> refs, int count)
|
||||||
{
|
{
|
||||||
int numLeft = m_maxPolys - m_numCollected;
|
int numLeft = m_maxPolys - m_numCollected;
|
||||||
int toCopy = count;
|
int toCopy = count;
|
||||||
|
|
|
@ -3,7 +3,7 @@ using DotRecast.Core.Numerics;
|
||||||
|
|
||||||
namespace DotRecast.Detour
|
namespace DotRecast.Detour
|
||||||
{
|
{
|
||||||
public class DtFindNearestPolyQuery : IDtPolyQuery
|
public struct DtFindNearestPolyQuery : IDtPolyQuery
|
||||||
{
|
{
|
||||||
private readonly DtNavMeshQuery _query;
|
private readonly DtNavMeshQuery _query;
|
||||||
private readonly RcVec3f _center;
|
private readonly RcVec3f _center;
|
||||||
|
@ -18,9 +18,12 @@ namespace DotRecast.Detour
|
||||||
_center = center;
|
_center = center;
|
||||||
_nearestDistanceSqr = float.MaxValue;
|
_nearestDistanceSqr = float.MaxValue;
|
||||||
_nearestPoint = center;
|
_nearestPoint = center;
|
||||||
|
|
||||||
|
_nearestRef = default;
|
||||||
|
_overPoly = default;
|
||||||
}
|
}
|
||||||
|
|
||||||
public void Process(DtMeshTile tile, DtPoly[] poly, Span<long> refs, int count)
|
public void Process(DtMeshTile tile, Span<DtPoly> poly, Span<long> refs, int count)
|
||||||
{
|
{
|
||||||
for (int i = 0; i < count; ++i)
|
for (int i = 0; i < count; ++i)
|
||||||
{
|
{
|
||||||
|
|
|
@ -0,0 +1,45 @@
|
||||||
|
using System;
|
||||||
|
using DotRecast.Core;
|
||||||
|
using DotRecast.Core.Numerics;
|
||||||
|
|
||||||
|
namespace DotRecast.Detour
|
||||||
|
{
|
||||||
|
public struct DtHeightSamplePolyQuery : IDtPolyQuery
|
||||||
|
{
|
||||||
|
private readonly DtNavMeshQuery _navMeshQuery;
|
||||||
|
private readonly RcVec3f _pt;
|
||||||
|
private readonly float _maxHeight;
|
||||||
|
public float MinHeight { get; private set; }
|
||||||
|
public bool Found { get; private set; }
|
||||||
|
|
||||||
|
public DtHeightSamplePolyQuery(DtNavMeshQuery navMeshQuery, RcVec3f pt, float minHeight, float maxHeight)
|
||||||
|
{
|
||||||
|
_navMeshQuery = navMeshQuery;
|
||||||
|
_pt = pt;
|
||||||
|
MinHeight = minHeight;
|
||||||
|
_maxHeight = maxHeight;
|
||||||
|
Found = default;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void Process(DtMeshTile tile, Span<DtPoly> poly, Span<long> refs, int count)
|
||||||
|
{
|
||||||
|
for (int i = 0; i < count; i++)
|
||||||
|
{
|
||||||
|
ProcessSingle(refs[i]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void ProcessSingle(long refs)
|
||||||
|
{
|
||||||
|
var status = _navMeshQuery.GetPolyHeight(refs, _pt, out var h);
|
||||||
|
if (!status.Succeeded())
|
||||||
|
return;
|
||||||
|
|
||||||
|
if (!(h > MinHeight) || !(h < _maxHeight))
|
||||||
|
return;
|
||||||
|
|
||||||
|
MinHeight = h;
|
||||||
|
Found = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -1365,6 +1365,11 @@ namespace DotRecast.Detour
|
||||||
}
|
}
|
||||||
|
|
||||||
public int GetTilesAt(int x, int y, DtMeshTile[] tiles, int maxTiles)
|
public int GetTilesAt(int x, int y, DtMeshTile[] tiles, int maxTiles)
|
||||||
|
{
|
||||||
|
return GetTilesAt(x, y, (Span<DtMeshTile>)tiles, maxTiles);
|
||||||
|
}
|
||||||
|
|
||||||
|
public int GetTilesAt(int x, int y, Span<DtMeshTile> tiles, int maxTiles)
|
||||||
{
|
{
|
||||||
int n = 0;
|
int n = 0;
|
||||||
|
|
||||||
|
|
|
@ -589,7 +589,7 @@ namespace DotRecast.Detour
|
||||||
|
|
||||||
// Get nearby polygons from proximity grid.
|
// Get nearby polygons from proximity grid.
|
||||||
DtFindNearestPolyQuery query = new DtFindNearestPolyQuery(this, center);
|
DtFindNearestPolyQuery query = new DtFindNearestPolyQuery(this, center);
|
||||||
DtStatus status = QueryPolygons(center, halfExtents, filter, query);
|
DtStatus status = QueryPolygons(center, halfExtents, filter, ref query);
|
||||||
if (status.Failed())
|
if (status.Failed())
|
||||||
{
|
{
|
||||||
return status;
|
return status;
|
||||||
|
@ -603,11 +603,13 @@ namespace DotRecast.Detour
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Queries polygons within a tile.
|
/// Queries polygons within a tile.
|
||||||
protected void QueryPolygonsInTile(DtMeshTile tile, RcVec3f qmin, RcVec3f qmax, IDtQueryFilter filter, IDtPolyQuery query)
|
protected void QueryPolygonsInTile<TQuery>(DtMeshTile tile, RcVec3f qmin, RcVec3f qmax, IDtQueryFilter filter, ref TQuery query)
|
||||||
|
where TQuery : IDtPolyQuery
|
||||||
{
|
{
|
||||||
const int batchSize = 32;
|
const int batchSize = 32;
|
||||||
Span<long> polyRefs = stackalloc long[batchSize];
|
Span<long> polyRefs = stackalloc long[batchSize];
|
||||||
DtPoly[] polys = new DtPoly[batchSize];
|
using RcRentedArray<DtPoly> polysRent = RcRentedArray.Rent<DtPoly>(batchSize);
|
||||||
|
Span<DtPoly> polys = polysRent.AsSpan();
|
||||||
int n = 0;
|
int n = 0;
|
||||||
|
|
||||||
if (tile.data.bvTree != null)
|
if (tile.data.bvTree != null)
|
||||||
|
@ -758,7 +760,7 @@ namespace DotRecast.Detour
|
||||||
return DtStatus.DT_FAILURE | DtStatus.DT_INVALID_PARAM;
|
return DtStatus.DT_FAILURE | DtStatus.DT_INVALID_PARAM;
|
||||||
|
|
||||||
DtCollectPolysQuery collector = new DtCollectPolysQuery(polys, maxPolys);
|
DtCollectPolysQuery collector = new DtCollectPolysQuery(polys, maxPolys);
|
||||||
DtStatus status = QueryPolygons(center, halfExtents, filter, collector);
|
DtStatus status = QueryPolygons(center, halfExtents, filter, ref collector);
|
||||||
if (status.Failed())
|
if (status.Failed())
|
||||||
return status;
|
return status;
|
||||||
|
|
||||||
|
@ -780,7 +782,8 @@ namespace DotRecast.Detour
|
||||||
/// @param[in] halfExtents The search distance along each axis. [(x, y, z)]
|
/// @param[in] halfExtents The search distance along each axis. [(x, y, z)]
|
||||||
/// @param[in] filter The polygon filter to apply to the query.
|
/// @param[in] filter The polygon filter to apply to the query.
|
||||||
/// @param[in] query The query. Polygons found will be batched together and passed to this query.
|
/// @param[in] query The query. Polygons found will be batched together and passed to this query.
|
||||||
public DtStatus QueryPolygons(RcVec3f center, RcVec3f halfExtents, IDtQueryFilter filter, IDtPolyQuery query)
|
public DtStatus QueryPolygons<TQuery>(RcVec3f center, RcVec3f halfExtents, IDtQueryFilter filter, ref TQuery query)
|
||||||
|
where TQuery : IDtPolyQuery
|
||||||
{
|
{
|
||||||
if (!center.IsFinite() || !halfExtents.IsFinite() || null == filter)
|
if (!center.IsFinite() || !halfExtents.IsFinite() || null == filter)
|
||||||
{
|
{
|
||||||
|
@ -796,7 +799,8 @@ namespace DotRecast.Detour
|
||||||
m_nav.CalcTileLoc(bmax, out var maxx, out var maxy);
|
m_nav.CalcTileLoc(bmax, out var maxx, out var maxy);
|
||||||
|
|
||||||
const int MAX_NEIS = 32;
|
const int MAX_NEIS = 32;
|
||||||
DtMeshTile[] neis = new DtMeshTile[MAX_NEIS];
|
using RcRentedArray<DtMeshTile> neisRent = RcRentedArray.Rent<DtMeshTile>(MAX_NEIS);
|
||||||
|
Span<DtMeshTile> neis = neisRent.AsSpan();
|
||||||
|
|
||||||
for (int y = miny; y <= maxy; ++y)
|
for (int y = miny; y <= maxy; ++y)
|
||||||
{
|
{
|
||||||
|
@ -805,7 +809,7 @@ namespace DotRecast.Detour
|
||||||
int nneis = m_nav.GetTilesAt(x, y, neis, MAX_NEIS);
|
int nneis = m_nav.GetTilesAt(x, y, neis, MAX_NEIS);
|
||||||
for (int j = 0; j < nneis; ++j)
|
for (int j = 0; j < nneis; ++j)
|
||||||
{
|
{
|
||||||
QueryPolygonsInTile(neis[j], bmin, bmax, filter, query);
|
QueryPolygonsInTile(neis[j], bmin, bmax, filter, ref query);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -9,6 +9,6 @@ namespace DotRecast.Detour
|
||||||
{
|
{
|
||||||
/// Called for each batch of unique polygons touched by the search area in dtNavMeshQuery::queryPolygons.
|
/// Called for each batch of unique polygons touched by the search area in dtNavMeshQuery::queryPolygons.
|
||||||
/// This can be called multiple times for a single query.
|
/// This can be called multiple times for a single query.
|
||||||
void Process(DtMeshTile tile, DtPoly[] poly, Span<long> refs, int count);
|
void Process(DtMeshTile tile, Span<DtPoly> poly, Span<long> refs, int count);
|
||||||
}
|
}
|
||||||
}
|
}
|
|
@ -103,4 +103,19 @@ public class RcRentedArrayTest
|
||||||
Assert.That(r1.IsDisposed, Is.EqualTo(true));
|
Assert.That(r1.IsDisposed, Is.EqualTo(true));
|
||||||
Assert.That(r1.AsArray(), Is.Null);
|
Assert.That(r1.AsArray(), Is.Null);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
[Test]
|
||||||
|
public void TestIdPoolCapacity()
|
||||||
|
{
|
||||||
|
var buffer = new RcRentedArray<int>[RcRentedArray.START_RENT_ID_POOL_CAPACITY + 2];
|
||||||
|
for (int i = 0; i < buffer.Length; i++)
|
||||||
|
{
|
||||||
|
Assert.DoesNotThrow(() => buffer[i] = RcRentedArray.Rent<int>(4));
|
||||||
|
}
|
||||||
|
|
||||||
|
for (int i = 0; i < buffer.Length; i++)
|
||||||
|
{
|
||||||
|
Assert.DoesNotThrow(() => buffer[i].Dispose());
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
Loading…
Reference in New Issue