Improve frame time stability by taking a few unnecessary repeated calculations out of the main scene loop.

Also uses a wait event to sleep rather than a Thread.Sleep to allow the loop to be interrupted in a more controlled manner when necessary.
ghosts
Justin Clark-Casey (justincc) 2014-09-26 20:05:22 +01:00
parent 1256d643be
commit 85e04198fe
2 changed files with 42 additions and 21 deletions

View File

@ -360,14 +360,31 @@ namespace OpenSim.Region.Framework.Scenes
public uint MaintenanceRun { get; private set; } public uint MaintenanceRun { get; private set; }
/// <summary> /// <summary>
/// The minimum length of time in seconds that will be taken for a scene frame. If the frame takes less time then we /// The minimum length of time in milliseconds that will be taken for a scene frame. If the frame takes less time then we
/// will sleep for the remaining period. /// will sleep for the remaining period.
/// </summary> /// </summary>
/// <remarks> /// <remarks>
/// One can tweak this number to experiment. One current effect of reducing it is to make avatar animations /// One can tweak this number to experiment. One current effect of reducing it is to make avatar animations
/// occur too quickly (viewer 1) or with even more slide (viewer 2). /// occur too quickly (viewer 1) or with even more slide (viewer 2).
/// </remarks> /// </remarks>
public float MinFrameTime { get; private set; } public int MinFrameTicks
{
get { return m_minFrameTicks; }
private set
{
m_minFrameTicks = value;
MinFrameSeconds = (float)m_minFrameTicks / 1000;
}
}
private int m_minFrameTicks;
/// <summary>
/// The minimum length of time in seconds that will be taken for a scene frame.
/// </summary>
/// <remarks>
/// Always derived from MinFrameTicks.
/// </remarks>
public float MinFrameSeconds { get; private set; }
/// <summary> /// <summary>
/// The minimum length of time in seconds that will be taken for a maintenance run. /// The minimum length of time in seconds that will be taken for a maintenance run.
@ -412,8 +429,11 @@ namespace OpenSim.Region.Framework.Scenes
/// asynchronously from the update loop. /// asynchronously from the update loop.
/// </summary> /// </summary>
private bool m_cleaningTemps = false; private bool m_cleaningTemps = false;
// private Object m_heartbeatLock = new Object(); /// <summary>
/// Used to control main scene thread looping time when not updating via timer.
/// </summary>
private ManualResetEvent m_updateWaitEvent = new ManualResetEvent(false);
// TODO: Possibly stop other classes being able to manipulate this directly. // TODO: Possibly stop other classes being able to manipulate this directly.
private SceneGraph m_sceneGraph; private SceneGraph m_sceneGraph;
@ -782,7 +802,6 @@ namespace OpenSim.Region.Framework.Scenes
: this(regInfo, physicsScene) : this(regInfo, physicsScene)
{ {
m_config = config; m_config = config;
MinFrameTime = 0.089f;
MinMaintenanceTime = 1; MinMaintenanceTime = 1;
SeeIntoRegion = true; SeeIntoRegion = true;
@ -1005,7 +1024,11 @@ namespace OpenSim.Region.Framework.Scenes
} }
} }
MinFrameTime = startupConfig.GetFloat( "MinFrameTime", MinFrameTime); if (startupConfig.Contains("MinFrameTime"))
MinFrameTicks = (int)(startupConfig.GetFloat("MinFrameTime") * 1000);
else
MinFrameTicks = 89;
m_update_backup = startupConfig.GetInt( "UpdateStorageEveryNFrames", m_update_backup); m_update_backup = startupConfig.GetInt( "UpdateStorageEveryNFrames", m_update_backup);
m_update_coarse_locations = startupConfig.GetInt( "UpdateCoarseLocationsEveryNFrames", m_update_coarse_locations); m_update_coarse_locations = startupConfig.GetInt( "UpdateCoarseLocationsEveryNFrames", m_update_coarse_locations);
m_update_entitymovement = startupConfig.GetInt( "UpdateEntityMovementEveryNFrames", m_update_entitymovement); m_update_entitymovement = startupConfig.GetInt( "UpdateEntityMovementEveryNFrames", m_update_entitymovement);
@ -1444,7 +1467,7 @@ namespace OpenSim.Region.Framework.Scenes
if (UpdateOnTimer) if (UpdateOnTimer)
{ {
m_sceneUpdateTimer = new Timer(MinFrameTime * 1000); m_sceneUpdateTimer = new Timer(MinFrameTicks);
m_sceneUpdateTimer.AutoReset = true; m_sceneUpdateTimer.AutoReset = true;
m_sceneUpdateTimer.Elapsed += Update; m_sceneUpdateTimer.Elapsed += Update;
m_sceneUpdateTimer.Start(); m_sceneUpdateTimer.Start();
@ -1534,7 +1557,7 @@ namespace OpenSim.Region.Framework.Scenes
runtc = (int)(MinMaintenanceTime * 1000) - runtc; runtc = (int)(MinMaintenanceTime * 1000) - runtc;
if (runtc > 0) if (runtc > 0)
Thread.Sleep(runtc); Thread.Sleep(runtc);
// Optionally warn if a frame takes double the amount of time that it should. // Optionally warn if a frame takes double the amount of time that it should.
if (DebugUpdates if (DebugUpdates
@ -1595,7 +1618,7 @@ namespace OpenSim.Region.Framework.Scenes
if (Frame % m_update_physics == 0) if (Frame % m_update_physics == 0)
{ {
if (PhysicsEnabled) if (PhysicsEnabled)
physicsFPS = m_sceneGraph.UpdatePhysics(MinFrameTime); physicsFPS = m_sceneGraph.UpdatePhysics(MinFrameSeconds);
if (SynchronizeScene != null) if (SynchronizeScene != null)
SynchronizeScene(this); SynchronizeScene(this);
@ -1707,18 +1730,16 @@ namespace OpenSim.Region.Framework.Scenes
{ {
Watchdog.UpdateThread(); Watchdog.UpdateThread();
tmpMS = Util.EnvironmentTickCountSubtract(Util.EnvironmentTickCount(), m_lastFrameTick); spareMS = MinFrameTicks - Util.EnvironmentTickCountSubtract(m_lastFrameTick);
tmpMS = (int)(MinFrameTime * 1000) - tmpMS;
if (tmpMS > 0) if (spareMS > 0)
{ m_updateWaitEvent.WaitOne(spareMS);
spareMS = tmpMS; else
Thread.Sleep(tmpMS); spareMS = 0;
}
} }
else else
{ {
spareMS = Math.Max(0, (int)(MinFrameTime * 1000) - physicsMS2 - agentMS - physicsMS -otherMS); spareMS = Math.Max(0, MinFrameTicks - physicsMS2 - agentMS - physicsMS - otherMS);
} }
previousFrameTick = m_lastFrameTick; previousFrameTick = m_lastFrameTick;
@ -1741,11 +1762,11 @@ namespace OpenSim.Region.Framework.Scenes
// Optionally warn if a frame takes double the amount of time that it should. // Optionally warn if a frame takes double the amount of time that it should.
if (DebugUpdates if (DebugUpdates
&& Util.EnvironmentTickCountSubtract( && Util.EnvironmentTickCountSubtract(
m_lastFrameTick, previousFrameTick) > (int)(MinFrameTime * 1000 * 2)) m_lastFrameTick, previousFrameTick) > MinFrameTicks * 2)
m_log.WarnFormat( m_log.WarnFormat(
"[SCENE]: Frame took {0} ms (desired max {1} ms) in {2}", "[SCENE]: Frame took {0} ms (desired max {1} ms) in {2}",
Util.EnvironmentTickCountSubtract(m_lastFrameTick, previousFrameTick), Util.EnvironmentTickCountSubtract(m_lastFrameTick, previousFrameTick),
MinFrameTime * 1000, MinFrameTicks,
RegionInfo.RegionName); RegionInfo.RegionName);
} }

View File

@ -224,7 +224,7 @@ namespace OpenSim.Region.Framework.Scenes
public SimStatsReporter(Scene scene) public SimStatsReporter(Scene scene)
{ {
m_scene = scene; m_scene = scene;
m_reportedFpsCorrectionFactor = scene.MinFrameTime * m_nominalReportedFps; m_reportedFpsCorrectionFactor = scene.MinFrameSeconds * m_nominalReportedFps;
m_statsUpdateFactor = (float)(m_statsUpdatesEveryMS / 1000); m_statsUpdateFactor = (float)(m_statsUpdatesEveryMS / 1000);
ReportingRegion = scene.RegionInfo; ReportingRegion = scene.RegionInfo;
@ -239,7 +239,7 @@ namespace OpenSim.Region.Framework.Scenes
/// At the moment, we'll only report if a frame is over 120% of target, since commonly frames are a bit /// At the moment, we'll only report if a frame is over 120% of target, since commonly frames are a bit
/// longer than ideal (which in itself is a concern). /// longer than ideal (which in itself is a concern).
SlowFramesStatReportThreshold = (int)Math.Ceiling(m_scene.MinFrameTime * 1000 * 1.2); SlowFramesStatReportThreshold = (int)Math.Ceiling(scene.MinFrameTicks * 1.2);
SlowFramesStat SlowFramesStat
= new Stat( = new Stat(