Move frame loop entirely within Scene.Update() for better future performance analysis and stat accuracy.

Update() now accepts a frames parameter which can control the number of frames updated.
-1 will update until shutdown.
The watchdog updating moves above the maintc recalculation for any required sleep since it should be accounted for within the frame.
0.7.3-extended
Justin Clark-Casey (justincc) 2012-03-20 23:12:21 +00:00
parent 3117b3cd88
commit 279b31c75b
8 changed files with 221 additions and 187 deletions

View File

@ -1753,6 +1753,20 @@ namespace OpenSim.Framework
} }
const Int32 EnvironmentTickCountMask = 0x3fffffff; const Int32 EnvironmentTickCountMask = 0x3fffffff;
/// <summary>
/// Environment.TickCount is an int but it counts all 32 bits so it goes positive
/// and negative every 24.9 days. Subtracts the passed value (previously fetched by
/// 'EnvironmentTickCount()') and accounts for any wrapping.
/// </summary>
/// <param name="newValue"></param>
/// <param name="prevValue"></param>
/// <returns>subtraction of passed prevValue from current Environment.TickCount</returns>
public static Int32 EnvironmentTickCountSubtract(Int32 newValue, Int32 prevValue)
{
Int32 diff = newValue - prevValue;
return (diff >= 0) ? diff : (diff + EnvironmentTickCountMask + 1);
}
/// <summary> /// <summary>
/// Environment.TickCount is an int but it counts all 32 bits so it goes positive /// Environment.TickCount is an int but it counts all 32 bits so it goes positive
/// and negative every 24.9 days. Subtracts the passed value (previously fetched by /// and negative every 24.9 days. Subtracts the passed value (previously fetched by
@ -1761,8 +1775,7 @@ namespace OpenSim.Framework
/// <returns>subtraction of passed prevValue from current Environment.TickCount</returns> /// <returns>subtraction of passed prevValue from current Environment.TickCount</returns>
public static Int32 EnvironmentTickCountSubtract(Int32 prevValue) public static Int32 EnvironmentTickCountSubtract(Int32 prevValue)
{ {
Int32 diff = EnvironmentTickCount() - prevValue; return EnvironmentTickCountSubtract(EnvironmentTickCount(), prevValue);
return (diff >= 0) ? diff : (diff + EnvironmentTickCountMask + 1);
} }
// Returns value of Tick Count A - TickCount B accounting for wrapping of TickCount // Returns value of Tick Count A - TickCount B accounting for wrapping of TickCount

View File

@ -50,7 +50,7 @@ namespace OpenSim.Region.ClientStack.LindenUDP.Tests
m_regStatus = RegionStatus.Up; m_regStatus = RegionStatus.Up;
} }
public override void Update() {} public override void Update(int frames) {}
public override void LoadWorldMap() {} public override void LoadWorldMap() {}
public override ISceneAgent AddNewClient(IClientAPI client, PresenceType type) public override ISceneAgent AddNewClient(IClientAPI client, PresenceType type)

View File

@ -1183,11 +1183,11 @@ namespace OpenSim.Region.Framework.Scenes
// The first frame can take a very long time due to physics actors being added on startup. Therefore, // The first frame can take a very long time due to physics actors being added on startup. Therefore,
// don't turn on the watchdog alarm for this thread until the second frame, in order to prevent false // don't turn on the watchdog alarm for this thread until the second frame, in order to prevent false
// alarms for scenes with many objects. // alarms for scenes with many objects.
Update(); Update(1);
Watchdog.GetCurrentThreadInfo().AlarmIfTimeout = true; Watchdog.GetCurrentThreadInfo().AlarmIfTimeout = true;
while (!shuttingdown) while (!shuttingdown)
Update(); Update(-1);
// m_lastUpdate = Util.EnvironmentTickCount(); // m_lastUpdate = Util.EnvironmentTickCount();
// m_firstHeartbeat = false; // m_firstHeartbeat = false;
@ -1201,34 +1201,45 @@ namespace OpenSim.Region.Framework.Scenes
Watchdog.RemoveThread(); Watchdog.RemoveThread();
} }
public override void Update() public override void Update(int frames)
{ {
long? endFrame = null;
if (frames >= 0)
endFrame = Frame + frames;
float physicsFPS = 0f; float physicsFPS = 0f;
int tmpFrameMS, tmpPhysicsMS, tmpPhysicsMS2, tmpAgentMS, tmpTempOnRezMS, evMS, backMS, terMS;
int maintc;
List<Vector3> coarseLocations;
List<UUID> avatarUUIDs;
int maintc = Util.EnvironmentTickCount(); while (!shuttingdown && (endFrame == null || Frame < endFrame))
int tmpFrameMS = maintc; {
agentMS = tempOnRezMS = eventMS = backupMS = terrainMS = landMS = 0; maintc = Util.EnvironmentTickCount();
++Frame; ++Frame;
// m_log.DebugFormat("[SCENE]: Processing frame {0} in {1}", Frame, RegionInfo.RegionName); // m_log.DebugFormat("[SCENE]: Processing frame {0} in {1}", Frame, RegionInfo.RegionName);
tmpFrameMS = maintc;
agentMS = tempOnRezMS = eventMS = backupMS = terrainMS = landMS = 0;
try try
{ {
int tmpPhysicsMS2 = Util.EnvironmentTickCount(); tmpPhysicsMS2 = Util.EnvironmentTickCount();
if ((Frame % m_update_physics == 0) && m_physics_enabled) if ((Frame % m_update_physics == 0) && m_physics_enabled)
m_sceneGraph.UpdatePreparePhysics(); m_sceneGraph.UpdatePreparePhysics();
physicsMS2 = Util.EnvironmentTickCountSubtract(tmpPhysicsMS2); physicsMS2 = Util.EnvironmentTickCountSubtract(tmpPhysicsMS2);
// Apply any pending avatar force input to the avatar's velocity // Apply any pending avatar force input to the avatar's velocity
int tmpAgentMS = Util.EnvironmentTickCount(); tmpAgentMS = Util.EnvironmentTickCount();
if (Frame % m_update_entitymovement == 0) if (Frame % m_update_entitymovement == 0)
m_sceneGraph.UpdateScenePresenceMovement(); m_sceneGraph.UpdateScenePresenceMovement();
agentMS = Util.EnvironmentTickCountSubtract(tmpAgentMS); agentMS = Util.EnvironmentTickCountSubtract(tmpAgentMS);
// Perform the main physics update. This will do the actual work of moving objects and avatars according to their // Perform the main physics update. This will do the actual work of moving objects and avatars according to their
// velocity // velocity
int tmpPhysicsMS = Util.EnvironmentTickCount(); tmpPhysicsMS = Util.EnvironmentTickCount();
if (Frame % m_update_physics == 0) if (Frame % m_update_physics == 0)
{ {
if (m_physics_enabled) if (m_physics_enabled)
@ -1257,8 +1268,6 @@ namespace OpenSim.Region.Framework.Scenes
// Coarse locations relate to positions of green dots on the mini-map (on a SecondLife client) // Coarse locations relate to positions of green dots on the mini-map (on a SecondLife client)
if (Frame % m_update_coarse_locations == 0) if (Frame % m_update_coarse_locations == 0)
{ {
List<Vector3> coarseLocations;
List<UUID> avatarUUIDs;
SceneGraph.GetCoarseLocations(out coarseLocations, out avatarUUIDs, 60); SceneGraph.GetCoarseLocations(out coarseLocations, out avatarUUIDs, 60);
// Send coarse locations to clients // Send coarse locations to clients
ForEachScenePresence(delegate(ScenePresence presence) ForEachScenePresence(delegate(ScenePresence presence)
@ -1272,7 +1281,7 @@ namespace OpenSim.Region.Framework.Scenes
// Delete temp-on-rez stuff // Delete temp-on-rez stuff
if (Frame % m_update_temp_cleaning == 0 && !m_cleaningTemps) if (Frame % m_update_temp_cleaning == 0 && !m_cleaningTemps)
{ {
int tmpTempOnRezMS = Util.EnvironmentTickCount(); tmpTempOnRezMS = Util.EnvironmentTickCount();
m_cleaningTemps = true; m_cleaningTemps = true;
Util.FireAndForget(delegate { CleanTempObjects(); m_cleaningTemps = false; }); Util.FireAndForget(delegate { CleanTempObjects(); m_cleaningTemps = false; });
tempOnRezMS = Util.EnvironmentTickCountSubtract(tmpTempOnRezMS); tempOnRezMS = Util.EnvironmentTickCountSubtract(tmpTempOnRezMS);
@ -1280,21 +1289,21 @@ namespace OpenSim.Region.Framework.Scenes
if (Frame % m_update_events == 0) if (Frame % m_update_events == 0)
{ {
int evMS = Util.EnvironmentTickCount(); evMS = Util.EnvironmentTickCount();
UpdateEvents(); UpdateEvents();
eventMS = Util.EnvironmentTickCountSubtract(evMS); ; eventMS = Util.EnvironmentTickCountSubtract(evMS);
} }
if (Frame % m_update_backup == 0) if (Frame % m_update_backup == 0)
{ {
int backMS = Util.EnvironmentTickCount(); backMS = Util.EnvironmentTickCount();
UpdateStorageBackup(); UpdateStorageBackup();
backupMS = Util.EnvironmentTickCountSubtract(backMS); backupMS = Util.EnvironmentTickCountSubtract(backMS);
} }
if (Frame % m_update_terrain == 0) if (Frame % m_update_terrain == 0)
{ {
int terMS = Util.EnvironmentTickCount(); terMS = Util.EnvironmentTickCount();
UpdateTerrain(); UpdateTerrain();
terrainMS = Util.EnvironmentTickCountSubtract(terMS); terrainMS = Util.EnvironmentTickCountSubtract(terMS);
} }
@ -1319,7 +1328,11 @@ namespace OpenSim.Region.Framework.Scenes
StatsReporter.SetChildAgents(m_sceneGraph.GetChildAgentCount()); StatsReporter.SetChildAgents(m_sceneGraph.GetChildAgentCount());
StatsReporter.SetObjects(m_sceneGraph.GetTotalObjectsCount()); StatsReporter.SetObjects(m_sceneGraph.GetTotalObjectsCount());
StatsReporter.SetActiveObjects(m_sceneGraph.GetActiveObjectsCount()); StatsReporter.SetActiveObjects(m_sceneGraph.GetActiveObjectsCount());
// frameMS currently records work frame times, not total frame times (work + any required sleep to
// reach min frame time.
StatsReporter.addFrameMS(frameMS); StatsReporter.addFrameMS(frameMS);
StatsReporter.addAgentMS(agentMS); StatsReporter.addAgentMS(agentMS);
StatsReporter.addPhysicsMS(physicsMS + physicsMS2); StatsReporter.addPhysicsMS(physicsMS + physicsMS2);
StatsReporter.addOtherMS(otherMS); StatsReporter.addOtherMS(otherMS);
@ -1328,7 +1341,7 @@ namespace OpenSim.Region.Framework.Scenes
if (LoginsDisabled && Frame == 20) if (LoginsDisabled && Frame == 20)
{ {
// m_log.DebugFormat("{0} {1} {2}", LoginsDisabled, m_sceneGraph.GetActiveScriptsCount(), LoginLock); // m_log.DebugFormat("{0} {1} {2}", LoginsDisabled, m_sceneGraph.GetActiveScriptsCount(), LoginLock);
// In 99.9% of cases it is a bad idea to manually force garbage collection. However, // In 99.9% of cases it is a bad idea to manually force garbage collection. However,
// this is a rare case where we know we have just went through a long cycle of heap // this is a rare case where we know we have just went through a long cycle of heap
@ -1371,14 +1384,22 @@ namespace OpenSim.Region.Framework.Scenes
EventManager.TriggerRegionHeartbeatEnd(this); EventManager.TriggerRegionHeartbeatEnd(this);
// Tell the watchdog that this thread is still alive
Watchdog.UpdateThread();
maintc = Util.EnvironmentTickCountSubtract(maintc); maintc = Util.EnvironmentTickCountSubtract(maintc);
maintc = (int)(MinFrameTime * 1000) - maintc; maintc = (int)(MinFrameTime * 1000) - maintc;
if (maintc > 0) if (maintc > 0)
Thread.Sleep(maintc); Thread.Sleep(maintc);
// Tell the watchdog that this thread is still alive // if (frameMS > (int)(MinFrameTime * 1000))
Watchdog.UpdateThread(); // m_log.WarnFormat(
// "[SCENE]: Frame took {0} ms (desired max {1} ms) in {2}",
// frameMS,
// MinFrameTime * 1000,
// RegionInfo.RegionName);
}
} }
public void AddGroupTarget(SceneObjectGroup grp) public void AddGroupTarget(SceneObjectGroup grp)

View File

@ -149,9 +149,13 @@ namespace OpenSim.Region.Framework.Scenes
#region Update Methods #region Update Methods
/// <summary> /// <summary>
/// Normally called once every frame/tick to let the world preform anything required (like running the physics simulation) /// Called to update the scene loop by a number of frames and until shutdown.
/// </summary> /// </summary>
public abstract void Update(); /// <param name="frames">
/// Number of frames to update. Exits on shutdown even if there are frames remaining.
/// If -1 then updates until shutdown.
/// </param>
public abstract void Update(int frames);
#endregion #endregion

View File

@ -81,7 +81,7 @@ namespace OpenSim.Region.Framework.Scenes.Tests
// For now, we'll make the scene presence fly to simplify this test, but this needs to change. // For now, we'll make the scene presence fly to simplify this test, but this needs to change.
sp.Flying = true; sp.Flying = true;
m_scene.Update(); m_scene.Update(1);
Assert.That(sp.AbsolutePosition, Is.EqualTo(startPos)); Assert.That(sp.AbsolutePosition, Is.EqualTo(startPos));
Vector3 targetPos = startPos + new Vector3(0, 10, 0); Vector3 targetPos = startPos + new Vector3(0, 10, 0);
@ -91,7 +91,7 @@ namespace OpenSim.Region.Framework.Scenes.Tests
Assert.That( Assert.That(
sp.Rotation, new QuaternionToleranceConstraint(new Quaternion(0, 0, 0.7071068f, 0.7071068f), 0.000001)); sp.Rotation, new QuaternionToleranceConstraint(new Quaternion(0, 0, 0.7071068f, 0.7071068f), 0.000001));
m_scene.Update(); m_scene.Update(1);
// We should really check the exact figure. // We should really check the exact figure.
Assert.That(sp.AbsolutePosition.X, Is.EqualTo(startPos.X)); Assert.That(sp.AbsolutePosition.X, Is.EqualTo(startPos.X));
@ -99,8 +99,7 @@ namespace OpenSim.Region.Framework.Scenes.Tests
Assert.That(sp.AbsolutePosition.Z, Is.EqualTo(startPos.Z)); Assert.That(sp.AbsolutePosition.Z, Is.EqualTo(startPos.Z));
Assert.That(sp.AbsolutePosition.Z, Is.LessThan(targetPos.X)); Assert.That(sp.AbsolutePosition.Z, Is.LessThan(targetPos.X));
for (int i = 0; i < 10; i++) m_scene.Update(10);
m_scene.Update();
double distanceToTarget = Util.GetDistanceTo(sp.AbsolutePosition, targetPos); double distanceToTarget = Util.GetDistanceTo(sp.AbsolutePosition, targetPos);
Assert.That(distanceToTarget, Is.LessThan(1), "Avatar not within 1 unit of target position on first move"); Assert.That(distanceToTarget, Is.LessThan(1), "Avatar not within 1 unit of target position on first move");
@ -116,7 +115,7 @@ namespace OpenSim.Region.Framework.Scenes.Tests
Assert.That( Assert.That(
sp.Rotation, new QuaternionToleranceConstraint(new Quaternion(0, 0, 0, 1), 0.000001)); sp.Rotation, new QuaternionToleranceConstraint(new Quaternion(0, 0, 0, 1), 0.000001));
m_scene.Update(); m_scene.Update(1);
// We should really check the exact figure. // We should really check the exact figure.
Assert.That(sp.AbsolutePosition.X, Is.GreaterThan(startPos.X)); Assert.That(sp.AbsolutePosition.X, Is.GreaterThan(startPos.X));
@ -124,8 +123,7 @@ namespace OpenSim.Region.Framework.Scenes.Tests
Assert.That(sp.AbsolutePosition.Y, Is.EqualTo(startPos.Y)); Assert.That(sp.AbsolutePosition.Y, Is.EqualTo(startPos.Y));
Assert.That(sp.AbsolutePosition.Z, Is.EqualTo(startPos.Z)); Assert.That(sp.AbsolutePosition.Z, Is.EqualTo(startPos.Z));
for (int i = 0; i < 10; i++) m_scene.Update(10);
m_scene.Update();
distanceToTarget = Util.GetDistanceTo(sp.AbsolutePosition, targetPos); distanceToTarget = Util.GetDistanceTo(sp.AbsolutePosition, targetPos);
Assert.That(distanceToTarget, Is.LessThan(1), "Avatar not within 1 unit of target position on second move"); Assert.That(distanceToTarget, Is.LessThan(1), "Avatar not within 1 unit of target position on second move");

View File

@ -61,7 +61,7 @@ namespace OpenSim.Region.Framework.Scenes.Tests
TestHelpers.InMethod(); TestHelpers.InMethod();
Scene scene = SceneHelpers.SetupScene(); Scene scene = SceneHelpers.SetupScene();
scene.Update(); scene.Update(1);
Assert.That(scene.Frame, Is.EqualTo(1)); Assert.That(scene.Frame, Is.EqualTo(1));
} }

View File

@ -238,7 +238,7 @@ namespace OpenSim.Region.OptionalModules.World.NPC.Tests
// For now, we'll make the scene presence fly to simplify this test, but this needs to change. // For now, we'll make the scene presence fly to simplify this test, but this needs to change.
npc.Flying = true; npc.Flying = true;
m_scene.Update(); m_scene.Update(1);
Assert.That(npc.AbsolutePosition, Is.EqualTo(startPos)); Assert.That(npc.AbsolutePosition, Is.EqualTo(startPos));
Vector3 targetPos = startPos + new Vector3(0, 10, 0); Vector3 targetPos = startPos + new Vector3(0, 10, 0);
@ -249,7 +249,7 @@ namespace OpenSim.Region.OptionalModules.World.NPC.Tests
Assert.That( Assert.That(
npc.Rotation, new QuaternionToleranceConstraint(new Quaternion(0, 0, 0.7071068f, 0.7071068f), 0.000001)); npc.Rotation, new QuaternionToleranceConstraint(new Quaternion(0, 0, 0.7071068f, 0.7071068f), 0.000001));
m_scene.Update(); m_scene.Update(1);
// We should really check the exact figure. // We should really check the exact figure.
Assert.That(npc.AbsolutePosition.X, Is.EqualTo(startPos.X)); Assert.That(npc.AbsolutePosition.X, Is.EqualTo(startPos.X));
@ -257,8 +257,7 @@ namespace OpenSim.Region.OptionalModules.World.NPC.Tests
Assert.That(npc.AbsolutePosition.Z, Is.EqualTo(startPos.Z)); Assert.That(npc.AbsolutePosition.Z, Is.EqualTo(startPos.Z));
Assert.That(npc.AbsolutePosition.Z, Is.LessThan(targetPos.X)); Assert.That(npc.AbsolutePosition.Z, Is.LessThan(targetPos.X));
for (int i = 0; i < 10; i++) m_scene.Update(10);
m_scene.Update();
double distanceToTarget = Util.GetDistanceTo(npc.AbsolutePosition, targetPos); double distanceToTarget = Util.GetDistanceTo(npc.AbsolutePosition, targetPos);
Assert.That(distanceToTarget, Is.LessThan(1), "NPC not within 1 unit of target position on first move"); Assert.That(distanceToTarget, Is.LessThan(1), "NPC not within 1 unit of target position on first move");
@ -275,7 +274,7 @@ namespace OpenSim.Region.OptionalModules.World.NPC.Tests
Assert.That( Assert.That(
npc.Rotation, new QuaternionToleranceConstraint(new Quaternion(0, 0, 0, 1), 0.000001)); npc.Rotation, new QuaternionToleranceConstraint(new Quaternion(0, 0, 0, 1), 0.000001));
m_scene.Update(); m_scene.Update(1);
// We should really check the exact figure. // We should really check the exact figure.
Assert.That(npc.AbsolutePosition.X, Is.GreaterThan(startPos.X)); Assert.That(npc.AbsolutePosition.X, Is.GreaterThan(startPos.X));
@ -283,8 +282,7 @@ namespace OpenSim.Region.OptionalModules.World.NPC.Tests
Assert.That(npc.AbsolutePosition.Y, Is.EqualTo(startPos.Y)); Assert.That(npc.AbsolutePosition.Y, Is.EqualTo(startPos.Y));
Assert.That(npc.AbsolutePosition.Z, Is.EqualTo(startPos.Z)); Assert.That(npc.AbsolutePosition.Z, Is.EqualTo(startPos.Z));
for (int i = 0; i < 10; i++) m_scene.Update(10);
m_scene.Update();
distanceToTarget = Util.GetDistanceTo(npc.AbsolutePosition, targetPos); distanceToTarget = Util.GetDistanceTo(npc.AbsolutePosition, targetPos);
Assert.That(distanceToTarget, Is.LessThan(1), "NPC not within 1 unit of target position on second move"); Assert.That(distanceToTarget, Is.LessThan(1), "NPC not within 1 unit of target position on second move");

View File

@ -154,7 +154,7 @@ namespace OpenSim.Tests.Torture
// //
// However, that means that we need to manually run an update here to clear out that list so that deleted // However, that means that we need to manually run an update here to clear out that list so that deleted
// objects will be clean up by the garbage collector before the next stress test is run. // objects will be clean up by the garbage collector before the next stress test is run.
scene.Update(); scene.Update(1);
// Currently, we need to do this in order to garbage collect the scene objects ready for the next test run. // Currently, we need to do this in order to garbage collect the scene objects ready for the next test run.
// However, what we really need to do is find out why the entire scene is not garbage collected in // However, what we really need to do is find out why the entire scene is not garbage collected in