Compare commits

..

6 Commits

Author SHA1 Message Date
wrenge 96ffed87e3 Rented array struct 2024-11-26 20:50:44 +03:00
wrenge 1315de063f Replaced arrays with spans in query. Replaced array allocation with buffer rent in query. 2024-11-26 20:05:00 +03:00
wrenge a6db4344e4 Queries change
(cherry picked from commit 2397f23fc3)
2024-11-26 20:00:22 +03:00
wrenge a7b9b772c4 Use rented array instead of allocating
(cherry picked from commit ff930712ee)
2024-11-26 19:59:47 +03:00
wrenge 7aeb31d369 Span access 2024-11-26 19:59:22 +03:00
wrenge 18b544845f Pass delegate 2024-11-26 19:54:21 +03:00
23 changed files with 115 additions and 292 deletions

View File

@ -1,8 +0,0 @@
{
"name": "rnd/dotrecastnetsim",
"description": "DotRecast",
"homepage": "https://git.bit5.ru/rnd/DotRecastNetSim.git",
"require": {
"php": ">=7.4"
}
}

View File

@ -1,15 +1,78 @@
using System;
using System.Buffers;
using System.Collections.Generic;
using System.Runtime.CompilerServices;
using System.Threading;
namespace DotRecast.Core.Buffers
{
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)
{
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 readonly struct RentIdGen
{
public readonly int Id;
public readonly int Gen;
public RentIdGen(int id, int gen)
{
Id = id;
Gen = gen;
}
}
@ -17,15 +80,17 @@ namespace DotRecast.Core.Buffers
{
private ArrayPool<T> _owner;
private T[] _array;
private readonly RentIdGen _rentIdGen;
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;
_array = array;
Length = length;
_rentIdGen = rentIdGen;
}
public ref T this[int index]
@ -34,6 +99,8 @@ namespace DotRecast.Core.Buffers
get
{
RcThrowHelper.ThrowExceptionIfIndexOutOfRange(index, Length);
if (IsDisposed)
throw new NullReferenceException();
return ref _array[index];
}
}
@ -51,12 +118,14 @@ namespace DotRecast.Core.Buffers
public void Dispose()
{
if (null != _owner && null != _array)
if (null != _owner && null != _array && !RcRentedArray.IsDisposed(_rentIdGen))
{
RcRentedArray.ReturnId(_rentIdGen);
_owner.Return(_array, true);
_owner = null;
_array = null;
}
_owner = null;
_array = null;
}
}
}

View File

@ -1,14 +0,0 @@
{
"name": "DotRecast.Core",
"rootNamespace": "DotRecast.Core",
"references": [],
"includePlatforms": [],
"excludePlatforms": [],
"allowUnsafeCode": false,
"overrideReferences": false,
"precompiledReferences": [],
"autoReferenced": true,
"defineConstraints": [],
"versionDefines": [],
"noEngineReferences": true
}

View File

@ -1,17 +0,0 @@
{
"name": "DotRecast.Detour.Crowd",
"rootNamespace": "DotRecast.Detour.Crowd",
"references": [
"DotRecast.Core",
"DotRecast.Detour"
],
"includePlatforms": [],
"excludePlatforms": [],
"allowUnsafeCode": false,
"overrideReferences": false,
"precompiledReferences": [],
"autoReferenced": true,
"defineConstraints": [],
"versionDefines": [],
"noEngineReferences": true
}

View File

@ -22,7 +22,6 @@ using System;
using System.Collections.Generic;
using System.Diagnostics;
using DotRecast.Core;
using DotRecast.Core.Buffers;
using DotRecast.Core.Collections;
using DotRecast.Core.Numerics;
@ -1057,7 +1056,7 @@ namespace DotRecast.Detour.Crowd
}
// Check
float triggerRadius = ag.option.radius * 0.25f;//todo make parameterizable
float triggerRadius = ag.option.radius * 2.25f;
if (ag.OverOffmeshConnection(triggerRadius))
{
// Prepare to off-mesh connection.
@ -1206,9 +1205,6 @@ namespace DotRecast.Detour.Crowd
for (int j = 0; j < ag.nneis; ++j)
{
DtCrowdAgent nei = ag.neis[j].agent;
if(!nei.option.contributeObstacleAvoidance || nei.option.obstacleAvoidanceWeight < ag.option.obstacleAvoidanceWeight)
continue;
_obstacleQuery.AddCircle(nei.npos, nei.option.radius, nei.vel, nei.dvel);
}

View File

@ -45,9 +45,6 @@ namespace DotRecast.Detour.Crowd
/// [Limits: 0 <= value < #DT_CROWD_MAX_OBSTAVOIDANCE_PARAMS]
public int obstacleAvoidanceType;
public bool contributeObstacleAvoidance;
public float obstacleAvoidanceWeight;
/// The index of the query filter used by this agent.
public int queryFilterType;

View File

@ -1,18 +0,0 @@
{
"name": "DotRecast.Detour.Dynamic",
"rootNamespace": "DotRecast.Detour.Dynamic",
"references": [
"DotRecast.Detour",
"DotRecast.Recast",
"DotRecast.Core"
],
"includePlatforms": [],
"excludePlatforms": [],
"allowUnsafeCode": false,
"overrideReferences": false,
"precompiledReferences": [],
"autoReferenced": true,
"defineConstraints": [],
"versionDefines": [],
"noEngineReferences": true
}

View File

@ -1,18 +0,0 @@
{
"name": "DotRecast.Detour.Extras",
"rootNamespace": "DotRecast.Detour.Extras",
"references": [
"DotRecast.Core",
"DotRecast.Detour",
"DotRecast.Recast"
],
"includePlatforms": [],
"excludePlatforms": [],
"allowUnsafeCode": false,
"overrideReferences": false,
"precompiledReferences": [],
"autoReferenced": true,
"defineConstraints": [],
"versionDefines": [],
"noEngineReferences": true
}

View File

@ -1,18 +0,0 @@
{
"name": "DotRecast.Detour.TileCache",
"rootNamespace": "DotRecast.Detour.TileCache",
"references": [
"DotRecast.Core",
"DotRecast.Detour",
"DotRecast.Recast"
],
"includePlatforms": [],
"excludePlatforms": [],
"allowUnsafeCode": false,
"overrideReferences": false,
"precompiledReferences": [],
"autoReferenced": true,
"defineConstraints": [],
"versionDefines": [],
"noEngineReferences": true
}

View File

@ -1,16 +0,0 @@
{
"name": "DotRecast.Detour",
"rootNamespace": "DotRecast.Detour",
"references": [
"DotRecast.Core"
],
"includePlatforms": [],
"excludePlatforms": [],
"allowUnsafeCode": false,
"overrideReferences": false,
"precompiledReferences": [],
"autoReferenced": true,
"defineConstraints": [],
"versionDefines": [],
"noEngineReferences": true
}

View File

@ -18,11 +18,8 @@ freely, subject to the following restrictions:
3. This notice may not be removed or altered from any source distribution.
*/
using System;
namespace DotRecast.Detour
{
[Serializable]
public class DtMeshData
{
public DtMeshHeader header; //< The tile header.

View File

@ -18,13 +18,11 @@ freely, subject to the following restrictions:
3. This notice may not be removed or altered from any source distribution.
*/
using System;
using DotRecast.Core.Numerics;
namespace DotRecast.Detour
{
/** Provides high level information related to a dtMeshTile object. */
[Serializable]
public class DtMeshHeader
{
/** Tile magic number. (Used to identify the data format.) */

View File

@ -18,7 +18,6 @@ freely, subject to the following restrictions:
3. This notice may not be removed or altered from any source distribution.
*/
using System;
using DotRecast.Core.Numerics;
namespace DotRecast.Detour

View File

@ -2541,7 +2541,7 @@ namespace DotRecast.Detour
// int va = a * 3;
// int vb = b * 3;
float dx = verts[b].X - verts[a].X;
float dz = verts[b].Z - verts[a].Z;
float dz = verts[b].Z - verts[a].X;
hit.hitNormal = RcVec3f.Normalize(new RcVec3f(dz, 0, -dx));
return DtStatus.DT_SUCCESS;
}

View File

@ -18,10 +18,8 @@ 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 System.Linq;
using DotRecast.Core.Buffers;
namespace DotRecast.Detour
{
@ -29,68 +27,24 @@ namespace DotRecast.Detour
{
private readonly Dictionary<long, List<DtNode>> m_map;
private int m_usedNodesCount;
private List<DtNode[]> m_buckets;
private readonly int m_initialBufferCapacityBase;
private readonly RcObjectPool<List<DtNode>> m_listPool;
private int m_nodeCount;
private readonly List<DtNode> m_nodes;
public DtNodePool(int initialBufferCapacityBase = 6) // initial size 64
public DtNodePool()
{
m_map = new Dictionary<long, List<DtNode>>();
m_listPool = new RcObjectPool<List<DtNode>>(() => new List<DtNode>());
m_buckets = new List<DtNode[]>();
m_initialBufferCapacityBase = initialBufferCapacityBase;
}
private void AddNewBucket()
{
var bucketIndex = m_buckets.Count;
var bucket = new DtNode[1 << (bucketIndex + m_initialBufferCapacityBase)];
m_buckets.Add(bucket);
FillBucket(bucketIndex);
}
private void FillBucket(int bucketIndex)
{
var bucket = m_buckets[bucketIndex];
var startIndex = GetBucketStartIndex(bucketIndex);
for (int i = 0; i < bucket.Length; i++)
{
bucket[i] = new DtNode(startIndex + i);
}
}
private int GetBucketStartIndex(int bucketIndex)
{
return ((1 << (bucketIndex + m_initialBufferCapacityBase)) - 1) ^ ((1 << m_initialBufferCapacityBase) - 1);
}
private DtNode[] EnsureBucket(int bucketIndex)
{
if (m_buckets.Count == bucketIndex)
AddNewBucket();
else if (m_buckets.Count < bucketIndex)
throw new Exception();
return m_buckets[bucketIndex];
}
private int GetBucketIndexByElementIndex(int elementIndex)
{
return DtUtils.Ilog2((elementIndex >> m_initialBufferCapacityBase) + 1);
m_nodes = new List<DtNode>();
}
public void Clear()
{
foreach (var pair in m_map)
m_listPool.Return(pair.Value);
m_map.Clear();
m_usedNodesCount = 0;
m_nodeCount = 0;
}
public int GetNodeCount()
{
return m_usedNodesCount;
return m_nodeCount;
}
public int FindNodes(long id, out List<DtNode> nodes)
@ -130,8 +84,7 @@ namespace DotRecast.Detour
}
else
{
nodes = m_listPool.Get();
nodes.Clear();
nodes = new List<DtNode>();
m_map.Add(id, nodes);
}
@ -140,10 +93,15 @@ namespace DotRecast.Detour
private DtNode Create(long id, int state, List<DtNode> nodes)
{
int i = m_usedNodesCount++;
int bucketIndex = GetBucketIndexByElementIndex(i);
int bucketStartIndex = GetBucketStartIndex(bucketIndex);
var node = EnsureBucket(bucketIndex)[i - bucketStartIndex];
if (m_nodes.Count <= m_nodeCount)
{
var newNode = new DtNode(m_nodeCount);
m_nodes.Add(newNode);
}
int i = m_nodeCount;
m_nodeCount++;
var node = m_nodes[i];
node.pidx = 0;
node.cost = 0;
node.total = 0;
@ -165,16 +123,9 @@ namespace DotRecast.Detour
public DtNode GetNodeAtIdx(int idx)
{
if (idx == 0)
return null;
int bucketIndex = GetBucketIndexByElementIndex(idx - 1);
if (m_buckets.Count <= bucketIndex)
throw new ArgumentOutOfRangeException();
int bucketStartIndex = GetBucketStartIndex(bucketIndex);
var node = EnsureBucket(bucketIndex)[idx - bucketStartIndex - 1];
return node;
return idx != 0
? m_nodes[idx - 1]
: null;
}
public DtNode GetNode(long refs)
@ -184,7 +135,7 @@ namespace DotRecast.Detour
public IEnumerable<DtNode> AsEnumerable()
{
return m_buckets.SelectMany(x => x);
return m_nodes.Take(m_nodeCount);
}
}
}

View File

@ -18,14 +18,12 @@ freely, subject to the following restrictions:
3. This notice may not be removed or altered from any source distribution.
*/
using System;
using DotRecast.Core.Numerics;
namespace DotRecast.Detour
{
/// Defines an navigation mesh off-mesh connection within a dtMeshTile object.
/// An off-mesh connection is a user defined traversable connection made up to two vertices.
[Serializable]
public class DtOffMeshConnection
{
/// The endpoints of the connection. [(ax, ay, az, bx, by, bz)]

View File

@ -1,44 +0,0 @@
{
"name": "DotRecast.Recast.Demo",
"rootNamespace": "DotRecast.Recast.Demo",
"references": [
"DotRecast.Core"
],
"includePlatforms": [],
"excludePlatforms": [
"Android",
"Editor",
"EmbeddedLinux",
"GameCoreScarlett",
"GameCoreXboxOne",
"iOS",
"LinuxStandalone64",
"CloudRendering",
"macOSStandalone",
"PS4",
"PS5",
"QNX",
"Stadia",
"Switch",
"tvOS",
"WSA",
"VisionOS",
"WebGL",
"WindowsStandalone32",
"WindowsStandalone64",
"XboxOne"
],
"allowUnsafeCode": false,
"overrideReferences": false,
"precompiledReferences": [],
"autoReferenced": true,
"defineConstraints": [],
"versionDefines": [
{
"name": "Select...",
"expression": "",
"define": "USE_RECAST_DEMO"
}
],
"noEngineReferences": true
}

View File

@ -1,22 +0,0 @@
{
"name": "DotRecast.Recast.Toolset",
"rootNamespace": "DotRecast.Recast.Toolset",
"references": [
"DotRecast.Core",
"DotRecast.Recast",
"DotRecast.Detour",
"DotRecast.Detour.Crowd",
"DotRecast.Detour.Dynamic",
"DotRecast.Detour.Extras",
"DotRecast.Detour.TileCache"
],
"includePlatforms": [],
"excludePlatforms": [],
"allowUnsafeCode": false,
"overrideReferences": false,
"precompiledReferences": [],
"autoReferenced": true,
"defineConstraints": [],
"versionDefines": [],
"noEngineReferences": true
}

View File

@ -46,13 +46,13 @@ namespace DotRecast.Recast.Toolset.Geom
if (null != _geom)
{
var offMeshConnections = _geom.GetOffMeshConnections();
option.offMeshConCount = offMeshConnections.Count;
option.offMeshConVerts = new float[option.offMeshConCount * 6];
option.offMeshConRad = new float[option.offMeshConCount];
option.offMeshConDir = new int[option.offMeshConCount];
option.offMeshConAreas = new int[option.offMeshConCount];
option.offMeshConFlags = new int[option.offMeshConCount];
option.offMeshConUserID = new int[option.offMeshConCount];
option.offMeshConCount = offMeshConnections.Count;
for (int i = 0; i < option.offMeshConCount; i++)
{
RcOffMeshConnection offMeshCon = offMeshConnections[i];
@ -65,7 +65,7 @@ namespace DotRecast.Recast.Toolset.Geom
option.offMeshConDir[i] = offMeshCon.bidir ? 1 : 0;
option.offMeshConAreas[i] = offMeshCon.area;
option.offMeshConFlags[i] = offMeshCon.flags;
option.offMeshConUserID[i] = offMeshCon.userId;
// option.offMeshConUserID[i] = offMeshCon.userId;
}
}
}

View File

@ -1,16 +0,0 @@
{
"name": "DotRecast.Recast",
"rootNamespace": "DotRecast.Recast",
"references": [
"DotRecast.Core"
],
"includePlatforms": [],
"excludePlatforms": [],
"allowUnsafeCode": false,
"overrideReferences": false,
"precompiledReferences": [],
"autoReferenced": true,
"defineConstraints": [],
"versionDefines": [],
"noEngineReferences": true
}

View File

@ -33,7 +33,7 @@ namespace DotRecast.Recast.Geom
public readonly int flags;
public readonly int userId;
public RcOffMeshConnection(RcVec3f start, RcVec3f end, float radius, bool bidir, int area, int flags, int userId = 0)
public RcOffMeshConnection(RcVec3f start, RcVec3f end, float radius, bool bidir, int area, int flags)
{
verts = new float[6];
verts[0] = start.X;
@ -46,7 +46,6 @@ namespace DotRecast.Recast.Geom
this.bidir = bidir;
this.area = area;
this.flags = flags;
this.userId = userId;
}
}
}

View File

@ -1,5 +0,0 @@
{
"name": "com.rnd.dotrecast",
"displayName": "DotRecast",
"version": "0.4.1"
}

View File

@ -103,4 +103,19 @@ public class RcRentedArrayTest
Assert.That(r1.IsDisposed, Is.EqualTo(true));
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());
}
}
}