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.4.1
Justin Clark-Casey (justincc) 2012-03-20 23:12:21 +00:00
parent 9ed3532c1b
commit 30b2a8c778
8 changed files with 221 additions and 187 deletions

View File

@ -1753,6 +1753,20 @@ namespace OpenSim.Framework
}
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>
/// 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
@ -1761,8 +1775,7 @@ namespace OpenSim.Framework
/// <returns>subtraction of passed prevValue from current Environment.TickCount</returns>
public static Int32 EnvironmentTickCountSubtract(Int32 prevValue)
{
Int32 diff = EnvironmentTickCount() - prevValue;
return (diff >= 0) ? diff : (diff + EnvironmentTickCountMask + 1);
return EnvironmentTickCountSubtract(EnvironmentTickCount(), prevValue);
}
// 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;
}
public override void Update() {}
public override void Update(int frames) {}
public override void LoadWorldMap() {}
public override ISceneAgent AddNewClient(IClientAPI client, PresenceType type)

View File

@ -1175,11 +1175,11 @@ namespace OpenSim.Region.Framework.Scenes
// 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
// alarms for scenes with many objects.
Update();
Update(1);
Watchdog.GetCurrentThreadInfo().AlarmIfTimeout = true;
while (!shuttingdown)
Update();
Update(-1);
m_lastUpdate = Util.EnvironmentTickCount();
m_firstHeartbeat = false;
@ -1193,34 +1193,45 @@ namespace OpenSim.Region.Framework.Scenes
Watchdog.RemoveThread();
}
public override void Update()
public override void Update(int frames)
{
long? endFrame = null;
if (frames >= 0)
endFrame = Frame + frames;
float physicsFPS = 0f;
int tmpFrameMS, tmpPhysicsMS, tmpPhysicsMS2, tmpAgentMS, tmpTempOnRezMS, evMS, backMS, terMS;
int maintc;
List<Vector3> coarseLocations;
List<UUID> avatarUUIDs;
int maintc = Util.EnvironmentTickCount();
int tmpFrameMS = maintc;
agentMS = tempOnRezMS = eventMS = backupMS = terrainMS = landMS = 0;
while (!shuttingdown && (endFrame == null || Frame < endFrame))
{
maintc = Util.EnvironmentTickCount();
++Frame;
// m_log.DebugFormat("[SCENE]: Processing frame {0} in {1}", Frame, RegionInfo.RegionName);
tmpFrameMS = maintc;
agentMS = tempOnRezMS = eventMS = backupMS = terrainMS = landMS = 0;
try
{
int tmpPhysicsMS2 = Util.EnvironmentTickCount();
tmpPhysicsMS2 = Util.EnvironmentTickCount();
if ((Frame % m_update_physics == 0) && m_physics_enabled)
m_sceneGraph.UpdatePreparePhysics();
physicsMS2 = Util.EnvironmentTickCountSubtract(tmpPhysicsMS2);
// Apply any pending avatar force input to the avatar's velocity
int tmpAgentMS = Util.EnvironmentTickCount();
tmpAgentMS = Util.EnvironmentTickCount();
if (Frame % m_update_entitymovement == 0)
m_sceneGraph.UpdateScenePresenceMovement();
agentMS = Util.EnvironmentTickCountSubtract(tmpAgentMS);
// Perform the main physics update. This will do the actual work of moving objects and avatars according to their
// velocity
int tmpPhysicsMS = Util.EnvironmentTickCount();
tmpPhysicsMS = Util.EnvironmentTickCount();
if (Frame % m_update_physics == 0)
{
if (m_physics_enabled)
@ -1249,8 +1260,6 @@ namespace OpenSim.Region.Framework.Scenes
// Coarse locations relate to positions of green dots on the mini-map (on a SecondLife client)
if (Frame % m_update_coarse_locations == 0)
{
List<Vector3> coarseLocations;
List<UUID> avatarUUIDs;
SceneGraph.GetCoarseLocations(out coarseLocations, out avatarUUIDs, 60);
// Send coarse locations to clients
ForEachScenePresence(delegate(ScenePresence presence)
@ -1264,7 +1273,7 @@ namespace OpenSim.Region.Framework.Scenes
// Delete temp-on-rez stuff
if (Frame % m_update_temp_cleaning == 0 && !m_cleaningTemps)
{
int tmpTempOnRezMS = Util.EnvironmentTickCount();
tmpTempOnRezMS = Util.EnvironmentTickCount();
m_cleaningTemps = true;
Util.FireAndForget(delegate { CleanTempObjects(); m_cleaningTemps = false; });
tempOnRezMS = Util.EnvironmentTickCountSubtract(tmpTempOnRezMS);
@ -1272,21 +1281,21 @@ namespace OpenSim.Region.Framework.Scenes
if (Frame % m_update_events == 0)
{
int evMS = Util.EnvironmentTickCount();
evMS = Util.EnvironmentTickCount();
UpdateEvents();
eventMS = Util.EnvironmentTickCountSubtract(evMS); ;
eventMS = Util.EnvironmentTickCountSubtract(evMS);
}
if (Frame % m_update_backup == 0)
{
int backMS = Util.EnvironmentTickCount();
backMS = Util.EnvironmentTickCount();
UpdateStorageBackup();
backupMS = Util.EnvironmentTickCountSubtract(backMS);
}
if (Frame % m_update_terrain == 0)
{
int terMS = Util.EnvironmentTickCount();
terMS = Util.EnvironmentTickCount();
UpdateTerrain();
terrainMS = Util.EnvironmentTickCountSubtract(terMS);
}
@ -1311,7 +1320,11 @@ namespace OpenSim.Region.Framework.Scenes
StatsReporter.SetChildAgents(m_sceneGraph.GetChildAgentCount());
StatsReporter.SetObjects(m_sceneGraph.GetTotalObjectsCount());
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.addAgentMS(agentMS);
StatsReporter.addPhysicsMS(physicsMS + physicsMS2);
StatsReporter.addOtherMS(otherMS);
@ -1320,7 +1333,7 @@ namespace OpenSim.Region.Framework.Scenes
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,
// this is a rare case where we know we have just went through a long cycle of heap
@ -1363,14 +1376,22 @@ namespace OpenSim.Region.Framework.Scenes
EventManager.TriggerRegionHeartbeatEnd(this);
// Tell the watchdog that this thread is still alive
Watchdog.UpdateThread();
maintc = Util.EnvironmentTickCountSubtract(maintc);
maintc = (int)(MinFrameTime * 1000) - maintc;
if (maintc > 0)
Thread.Sleep(maintc);
// Tell the watchdog that this thread is still alive
Watchdog.UpdateThread();
// if (frameMS > (int)(MinFrameTime * 1000))
// m_log.WarnFormat(
// "[SCENE]: Frame took {0} ms (desired max {1} ms) in {2}",
// frameMS,
// MinFrameTime * 1000,
// RegionInfo.RegionName);
}
}
public void AddGroupTarget(SceneObjectGroup grp)

View File

@ -149,9 +149,13 @@ namespace OpenSim.Region.Framework.Scenes
#region Update Methods
/// <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>
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

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.
sp.Flying = true;
m_scene.Update();
m_scene.Update(1);
Assert.That(sp.AbsolutePosition, Is.EqualTo(startPos));
Vector3 targetPos = startPos + new Vector3(0, 10, 0);
@ -91,7 +91,7 @@ namespace OpenSim.Region.Framework.Scenes.Tests
Assert.That(
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.
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.LessThan(targetPos.X));
for (int i = 0; i < 10; i++)
m_scene.Update();
m_scene.Update(10);
double distanceToTarget = Util.GetDistanceTo(sp.AbsolutePosition, targetPos);
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(
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.
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.Z, Is.EqualTo(startPos.Z));
for (int i = 0; i < 10; i++)
m_scene.Update();
m_scene.Update(10);
distanceToTarget = Util.GetDistanceTo(sp.AbsolutePosition, targetPos);
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();
Scene scene = SceneHelpers.SetupScene();
scene.Update();
scene.Update(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.
npc.Flying = true;
m_scene.Update();
m_scene.Update(1);
Assert.That(npc.AbsolutePosition, Is.EqualTo(startPos));
Vector3 targetPos = startPos + new Vector3(0, 10, 0);
@ -249,7 +249,7 @@ namespace OpenSim.Region.OptionalModules.World.NPC.Tests
Assert.That(
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.
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.LessThan(targetPos.X));
for (int i = 0; i < 10; i++)
m_scene.Update();
m_scene.Update(10);
double distanceToTarget = Util.GetDistanceTo(npc.AbsolutePosition, targetPos);
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(
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.
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.Z, Is.EqualTo(startPos.Z));
for (int i = 0; i < 10; i++)
m_scene.Update();
m_scene.Update(10);
distanceToTarget = Util.GetDistanceTo(npc.AbsolutePosition, targetPos);
Assert.That(distanceToTarget, Is.LessThan(1), "NPC not within 1 unit of target position on second move");

View File

@ -157,7 +157,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
// objects will be clean up by the garbage collector before the next stress test is run.
scene.Update();
scene.Update(1);
Console.WriteLine(
"Took {0}ms, {1}MB ({2} - {3}) to create {4} objects each containing {5} prim(s)",