Added avg, min, max, sampling updated times in CrowdAgentProfilingTool

This commit is contained in:
ikpil 2024-02-04 12:14:18 +09:00 committed by Ikpil
parent 6b033ab058
commit 2dc8593c78
3 changed files with 100 additions and 43 deletions

View File

@ -8,6 +8,7 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/).
### Added ### Added
- Added DtNodePool tests - Added DtNodePool tests
- Added avg, min, max, sampling updated times in CrowdAgentProfilingTool
### Fixed ### Fixed
- Fixed SOH issue in DtNavMeshQuery.Raycast - Fixed SOH issue in DtNavMeshQuery.Raycast

View File

@ -118,7 +118,11 @@ public class CrowdAgentProfilingSampleTool : ISampleTool
ImGui.Text($"{rtt.Key}: {rtt.Micros} us"); ImGui.Text($"{rtt.Key}: {rtt.Micros} us");
} }
ImGui.Text($"Update Time: {_tool.GetCrowdUpdateTime()} ms"); ImGui.Text($"Current Update Time: {_tool.GetCrowdUpdateTime()} ms");
ImGui.Text($"Sampling Update Time: {_tool.GetCrowdUpdateSamplingTime()} ms");
ImGui.Text($"Avg Update Time: {_tool.GetCrowdUpdateAvgTime()} ms");
ImGui.Text($"Max Update Time: {_tool.GetCrowdUpdateMaxTime()} ms");
ImGui.Text($"Min Update Time: {_tool.GetCrowdUpdateMinTime()} ms");
} }
} }

View File

@ -1,5 +1,6 @@
using System; using System;
using System.Collections.Generic; using System.Collections.Generic;
using System.Linq;
using DotRecast.Core; using DotRecast.Core;
using DotRecast.Core.Collections; using DotRecast.Core.Collections;
using DotRecast.Core.Numerics; using DotRecast.Core.Numerics;
@ -14,20 +15,28 @@ namespace DotRecast.Recast.Toolset.Tools
private RcCrowdAgentProfilingToolConfig _cfg; private RcCrowdAgentProfilingToolConfig _cfg;
private DtCrowdConfig _crowdCfg; private DtCrowdConfig _crowdCfg;
private DtCrowd crowd; private DtCrowd _crowd;
private readonly DtCrowdAgentConfig _agCfg; private readonly DtCrowdAgentConfig _agCfg;
private DtNavMesh navMesh; private DtNavMesh _navMesh;
private RcRand rnd; private IRcRand _rand;
private readonly List<DtPolyPoint> _polyPoints; private readonly List<DtPolyPoint> _polyPoints;
private long crowdUpdateTime;
private const int SamplingCount = 500;
private long _samplingUpdateTime;
private readonly List<long> _updateTimes;
private long _curUpdateTime;
private long _avgUpdateTime;
private long _minUpdateTime;
private long _maxUpdateTime;
public RcCrowdAgentProfilingTool() public RcCrowdAgentProfilingTool()
{ {
_cfg = new RcCrowdAgentProfilingToolConfig(); _cfg = new RcCrowdAgentProfilingToolConfig();
_agCfg = new DtCrowdAgentConfig(); _agCfg = new DtCrowdAgentConfig();
_polyPoints = new List<DtPolyPoint>(); _polyPoints = new List<DtPolyPoint>();
_updateTimes = new List<long>();
} }
public string GetName() public string GetName()
@ -47,12 +56,12 @@ namespace DotRecast.Recast.Toolset.Tools
public DtCrowd GetCrowd() public DtCrowd GetCrowd()
{ {
return crowd; return _crowd;
} }
public void Setup(float maxAgentRadius, DtNavMesh nav) public void Setup(float maxAgentRadius, DtNavMesh nav)
{ {
navMesh = nav; _navMesh = nav;
if (nav != null) if (nav != null)
{ {
_crowdCfg = new DtCrowdConfig(maxAgentRadius); _crowdCfg = new DtCrowdConfig(maxAgentRadius);
@ -76,7 +85,7 @@ namespace DotRecast.Recast.Toolset.Tools
private DtStatus GetMobPosition(DtNavMeshQuery navquery, IDtQueryFilter filter, out RcVec3f randomPt) private DtStatus GetMobPosition(DtNavMeshQuery navquery, IDtQueryFilter filter, out RcVec3f randomPt)
{ {
return navquery.FindRandomPoint(filter, rnd, out var randomRef, out randomPt); return navquery.FindRandomPoint(filter, _rand, out var randomRef, out randomPt);
} }
private DtStatus GetVillagerPosition(DtNavMeshQuery navquery, IDtQueryFilter filter, out RcVec3f randomPt) private DtStatus GetVillagerPosition(DtNavMeshQuery navquery, IDtQueryFilter filter, out RcVec3f randomPt)
@ -86,8 +95,8 @@ namespace DotRecast.Recast.Toolset.Tools
if (0 >= _polyPoints.Count) if (0 >= _polyPoints.Count)
return DtStatus.DT_FAILURE; return DtStatus.DT_FAILURE;
int zone = (int)(rnd.Next() * _polyPoints.Count); int zone = (int)(_rand.Next() * _polyPoints.Count);
return navquery.FindRandomPointWithinCircle(_polyPoints[zone].refs, _polyPoints[zone].pt, _cfg.zoneRadius, filter, rnd, return navquery.FindRandomPointWithinCircle(_polyPoints[zone].refs, _polyPoints[zone].pt, _cfg.zoneRadius, filter, _rand,
out var randomRef, out randomPt); out var randomRef, out randomPt);
} }
@ -95,13 +104,13 @@ namespace DotRecast.Recast.Toolset.Tools
{ {
_polyPoints.Clear(); _polyPoints.Clear();
IDtQueryFilter filter = new DtQueryDefaultFilter(); IDtQueryFilter filter = new DtQueryDefaultFilter();
DtNavMeshQuery navquery = new DtNavMeshQuery(navMesh); DtNavMeshQuery navquery = new DtNavMeshQuery(_navMesh);
for (int i = 0; i < _cfg.numberOfZones; i++) for (int i = 0; i < _cfg.numberOfZones; i++)
{ {
float zoneSeparation = _cfg.zoneRadius * _cfg.zoneRadius * 16; float zoneSeparation = _cfg.zoneRadius * _cfg.zoneRadius * 16;
for (int k = 0; k < 100; k++) for (int k = 0; k < 100; k++)
{ {
var status = navquery.FindRandomPoint(filter, rnd, out var randomRef, out var randomPt); var status = navquery.FindRandomPoint(filter, _rand, out var randomRef, out var randomPt);
if (status.Succeeded()) if (status.Succeeded())
{ {
bool valid = true; bool valid = true;
@ -126,57 +135,65 @@ namespace DotRecast.Recast.Toolset.Tools
private void CreateCrowd() private void CreateCrowd()
{ {
crowd = new DtCrowd(_crowdCfg, navMesh, __ => new DtQueryDefaultFilter( _crowd = new DtCrowd(_crowdCfg, _navMesh, __ => new DtQueryDefaultFilter(
SampleAreaModifications.SAMPLE_POLYFLAGS_ALL, SampleAreaModifications.SAMPLE_POLYFLAGS_ALL,
SampleAreaModifications.SAMPLE_POLYFLAGS_DISABLED, SampleAreaModifications.SAMPLE_POLYFLAGS_DISABLED,
new float[] { 1f, 10f, 1f, 1f, 2f, 1.5f }) new float[] { 1f, 10f, 1f, 1f, 2f, 1.5f })
); );
DtObstacleAvoidanceParams option = new DtObstacleAvoidanceParams(crowd.GetObstacleAvoidanceParams(0)); DtObstacleAvoidanceParams option = new DtObstacleAvoidanceParams(_crowd.GetObstacleAvoidanceParams(0));
// Low (11) // Low (11)
option.velBias = 0.5f; option.velBias = 0.5f;
option.adaptiveDivs = 5; option.adaptiveDivs = 5;
option.adaptiveRings = 2; option.adaptiveRings = 2;
option.adaptiveDepth = 1; option.adaptiveDepth = 1;
crowd.SetObstacleAvoidanceParams(0, option); _crowd.SetObstacleAvoidanceParams(0, option);
// Medium (22) // Medium (22)
option.velBias = 0.5f; option.velBias = 0.5f;
option.adaptiveDivs = 5; option.adaptiveDivs = 5;
option.adaptiveRings = 2; option.adaptiveRings = 2;
option.adaptiveDepth = 2; option.adaptiveDepth = 2;
crowd.SetObstacleAvoidanceParams(1, option); _crowd.SetObstacleAvoidanceParams(1, option);
// Good (45) // Good (45)
option.velBias = 0.5f; option.velBias = 0.5f;
option.adaptiveDivs = 7; option.adaptiveDivs = 7;
option.adaptiveRings = 2; option.adaptiveRings = 2;
option.adaptiveDepth = 3; option.adaptiveDepth = 3;
crowd.SetObstacleAvoidanceParams(2, option); _crowd.SetObstacleAvoidanceParams(2, option);
// High (66) // High (66)
option.velBias = 0.5f; option.velBias = 0.5f;
option.adaptiveDivs = 7; option.adaptiveDivs = 7;
option.adaptiveRings = 3; option.adaptiveRings = 3;
option.adaptiveDepth = 3; option.adaptiveDepth = 3;
crowd.SetObstacleAvoidanceParams(3, option); _crowd.SetObstacleAvoidanceParams(3, option);
} }
public void StartProfiling(float agentRadius, float agentHeight, float agentMaxAcceleration, float agentMaxSpeed) public void StartProfiling(float agentRadius, float agentHeight, float agentMaxAcceleration, float agentMaxSpeed)
{ {
if (null == navMesh) if (null == _navMesh)
return; return;
rnd = new RcRand(_cfg.randomSeed); // for benchmark
_updateTimes.Clear();
_samplingUpdateTime = 0;
_curUpdateTime = 0;
_avgUpdateTime = 0;
_minUpdateTime = 0;
_maxUpdateTime = 0;
_rand = new RcRand(_cfg.randomSeed);
CreateCrowd(); CreateCrowd();
CreateZones(); CreateZones();
DtNavMeshQuery navquery = new DtNavMeshQuery(navMesh); DtNavMeshQuery navquery = new DtNavMeshQuery(_navMesh);
IDtQueryFilter filter = new DtQueryDefaultFilter(); IDtQueryFilter filter = new DtQueryDefaultFilter();
for (int i = 0; i < _cfg.agents; i++) for (int i = 0; i < _cfg.agents; i++)
{ {
float tr = rnd.Next(); float tr = _rand.Next();
RcCrowdAgentType type = RcCrowdAgentType.MOB; RcCrowdAgentType type = RcCrowdAgentType.MOB;
float mobsPcnt = _cfg.percentMobs / 100f; float mobsPcnt = _cfg.percentMobs / 100f;
if (tr > mobsPcnt) if (tr > mobsPcnt)
{ {
tr = rnd.Next(); tr = _rand.Next();
float travellerPcnt = _cfg.percentTravellers / 100f; float travellerPcnt = _cfg.percentTravellers / 100f;
if (tr > travellerPcnt) if (tr > travellerPcnt)
{ {
@ -213,19 +230,19 @@ namespace DotRecast.Recast.Toolset.Tools
public void Update(float dt) public void Update(float dt)
{ {
long startTime = RcFrequency.Ticks; long startTime = RcFrequency.Ticks;
if (crowd != null) if (_crowd != null)
{ {
crowd.Config().pathQueueSize = _cfg.pathQueueSize; _crowd.Config().pathQueueSize = _cfg.pathQueueSize;
crowd.Config().maxFindPathIterations = _cfg.maxIterations; _crowd.Config().maxFindPathIterations = _cfg.maxIterations;
crowd.Update(dt, null); _crowd.Update(dt, null);
} }
long endTime = RcFrequency.Ticks; long endTime = RcFrequency.Ticks;
if (crowd != null) if (_crowd != null)
{ {
DtNavMeshQuery navquery = new DtNavMeshQuery(navMesh); DtNavMeshQuery navquery = new DtNavMeshQuery(_navMesh);
IDtQueryFilter filter = new DtQueryDefaultFilter(); IDtQueryFilter filter = new DtQueryDefaultFilter();
foreach (DtCrowdAgent ag in crowd.GetActiveAgents()) foreach (DtCrowdAgent ag in _crowd.GetActiveAgents())
{ {
if (NeedsNewTarget(ag)) if (NeedsNewTarget(ag))
{ {
@ -246,20 +263,34 @@ namespace DotRecast.Recast.Toolset.Tools
} }
} }
crowdUpdateTime = (endTime - startTime) / TimeSpan.TicksPerMillisecond; var currentTime = endTime - startTime;
_updateTimes.Add(currentTime);
if ((int)(SamplingCount * 1.25f) < _updateTimes.Count)
{
_updateTimes.RemoveRange(0, _updateTimes.Count - SamplingCount);
}
// for benchmark
_samplingUpdateTime = _updateTimes.Sum() / TimeSpan.TicksPerMillisecond;
_curUpdateTime = currentTime / TimeSpan.TicksPerMillisecond;
_avgUpdateTime = (long)(_updateTimes.Average() / TimeSpan.TicksPerMillisecond);
_minUpdateTime = _updateTimes.Min() / TimeSpan.TicksPerMillisecond;
_maxUpdateTime = _updateTimes.Max() / TimeSpan.TicksPerMillisecond;
} }
private void MoveMob(DtNavMeshQuery navquery, IDtQueryFilter filter, DtCrowdAgent ag, RcCrowdAgentData crowAgentData) private void MoveMob(DtNavMeshQuery navquery, IDtQueryFilter filter, DtCrowdAgent ag, RcCrowdAgentData crowAgentData)
{ {
// Move somewhere // Move somewhere
var status = navquery.FindNearestPoly(ag.npos, crowd.GetQueryExtents(), filter, out var nearestRef, out var nearestPt, out var _); var status = navquery.FindNearestPoly(ag.npos, _crowd.GetQueryExtents(), filter, out var nearestRef, out var nearestPt, out var _);
if (status.Succeeded()) if (status.Succeeded())
{ {
status = navquery.FindRandomPointAroundCircle(nearestRef, crowAgentData.home, _cfg.zoneRadius * 2f, filter, rnd, status = navquery.FindRandomPointAroundCircle(nearestRef, crowAgentData.home, _cfg.zoneRadius * 2f, filter, _rand,
out var randomRef, out var randomPt); out var randomRef, out var randomPt);
if (status.Succeeded()) if (status.Succeeded())
{ {
crowd.RequestMoveTarget(ag, randomRef, randomPt); _crowd.RequestMoveTarget(ag, randomRef, randomPt);
} }
} }
} }
@ -267,14 +298,14 @@ namespace DotRecast.Recast.Toolset.Tools
private void MoveVillager(DtNavMeshQuery navquery, IDtQueryFilter filter, DtCrowdAgent ag, RcCrowdAgentData crowAgentData) private void MoveVillager(DtNavMeshQuery navquery, IDtQueryFilter filter, DtCrowdAgent ag, RcCrowdAgentData crowAgentData)
{ {
// Move somewhere close // Move somewhere close
var status = navquery.FindNearestPoly(ag.npos, crowd.GetQueryExtents(), filter, out var nearestRef, out var nearestPt, out var _); var status = navquery.FindNearestPoly(ag.npos, _crowd.GetQueryExtents(), filter, out var nearestRef, out var nearestPt, out var _);
if (status.Succeeded()) if (status.Succeeded())
{ {
status = navquery.FindRandomPointAroundCircle(nearestRef, crowAgentData.home, _cfg.zoneRadius * 0.2f, filter, rnd, status = navquery.FindRandomPointAroundCircle(nearestRef, crowAgentData.home, _cfg.zoneRadius * 0.2f, filter, _rand,
out var randomRef, out var randomPt); out var randomRef, out var randomPt);
if (status.Succeeded()) if (status.Succeeded())
{ {
crowd.RequestMoveTarget(ag, randomRef, randomPt); _crowd.RequestMoveTarget(ag, randomRef, randomPt);
} }
} }
} }
@ -294,7 +325,7 @@ namespace DotRecast.Recast.Toolset.Tools
if (0 < potentialTargets.Count) if (0 < potentialTargets.Count)
{ {
potentialTargets.Shuffle(); potentialTargets.Shuffle();
crowd.RequestMoveTarget(ag, potentialTargets[0].refs, potentialTargets[0].pt); _crowd.RequestMoveTarget(ag, potentialTargets[0].refs, potentialTargets[0].pt);
} }
} }
@ -321,14 +352,14 @@ namespace DotRecast.Recast.Toolset.Tools
{ {
DtCrowdAgentParams ap = GetAgentParams(agentRadius, agentHeight, agentMaxAcceleration, agentMaxSpeed); DtCrowdAgentParams ap = GetAgentParams(agentRadius, agentHeight, agentMaxAcceleration, agentMaxSpeed);
ap.userData = new RcCrowdAgentData(type, p); ap.userData = new RcCrowdAgentData(type, p);
return crowd.AddAgent(p, ap); return _crowd.AddAgent(p, ap);
} }
public void UpdateAgentParams() public void UpdateAgentParams()
{ {
if (crowd != null) if (_crowd != null)
{ {
foreach (DtCrowdAgent ag in crowd.GetActiveAgents()) foreach (DtCrowdAgent ag in _crowd.GetActiveAgents())
{ {
DtCrowdAgentParams option = new DtCrowdAgentParams(); DtCrowdAgentParams option = new DtCrowdAgentParams();
option.radius = ag.option.radius; option.radius = ag.option.radius;
@ -342,14 +373,35 @@ namespace DotRecast.Recast.Toolset.Tools
option.updateFlags = _agCfg.GetUpdateFlags(); option.updateFlags = _agCfg.GetUpdateFlags();
option.obstacleAvoidanceType = _agCfg.obstacleAvoidanceType; option.obstacleAvoidanceType = _agCfg.obstacleAvoidanceType;
option.separationWeight = _agCfg.separationWeight; option.separationWeight = _agCfg.separationWeight;
crowd.UpdateAgentParameters(ag, option); _crowd.UpdateAgentParameters(ag, option);
} }
} }
} }
public long GetCrowdUpdateSamplingTime()
{
return _samplingUpdateTime;
}
public long GetCrowdUpdateTime() public long GetCrowdUpdateTime()
{ {
return crowdUpdateTime; return _curUpdateTime;
} }
public long GetCrowdUpdateAvgTime()
{
return _avgUpdateTime;
}
public long GetCrowdUpdateMinTime()
{
return _minUpdateTime;
}
public long GetCrowdUpdateMaxTime()
{
return _maxUpdateTime;
}
} }
} }