diff --git a/OpenSim/Region/Framework/Scenes/Scene.cs b/OpenSim/Region/Framework/Scenes/Scene.cs index 32c726266f..b79892d863 100644 --- a/OpenSim/Region/Framework/Scenes/Scene.cs +++ b/OpenSim/Region/Framework/Scenes/Scene.cs @@ -1512,10 +1512,6 @@ namespace OpenSim.Region.Framework.Scenes StatsReporter.AddPhysicsFPS(physicsFPS); StatsReporter.AddTimeDilation(TimeDilation); StatsReporter.AddFPS(1); - StatsReporter.SetRootAgents(m_sceneGraph.GetRootAgentCount()); - 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. @@ -1524,7 +1520,6 @@ namespace OpenSim.Region.Framework.Scenes StatsReporter.addAgentMS(agentMS); StatsReporter.addPhysicsMS(physicsMS + physicsMS2); StatsReporter.addOtherMS(otherMS); - StatsReporter.SetActiveScripts(m_sceneGraph.GetActiveScriptsCount()); StatsReporter.addScriptLines(m_sceneGraph.GetScriptLPS()); if (LoginsDisabled && Frame == 20) diff --git a/OpenSim/Region/Framework/Scenes/SimStatsReporter.cs b/OpenSim/Region/Framework/Scenes/SimStatsReporter.cs index cb102d09d5..0efe4c4db7 100644 --- a/OpenSim/Region/Framework/Scenes/SimStatsReporter.cs +++ b/OpenSim/Region/Framework/Scenes/SimStatsReporter.cs @@ -222,6 +222,10 @@ namespace OpenSim.Region.Framework.Scenes m_report.Close(); } + /// + /// Sets the number of milliseconds between stat updates. + /// + /// public void SetUpdateMS(int ms) { statsUpdatesEveryMS = ms; @@ -296,6 +300,16 @@ namespace OpenSim.Region.Framework.Scenes sparetime = TotalFrameTime; sleeptime = m_sleeptimeMS * perframe; } + + m_rootAgents = m_scene.SceneGraph.GetRootAgentCount(); + m_childAgents = m_scene.SceneGraph.GetChildAgentCount(); + m_numPrim = m_scene.SceneGraph.GetTotalObjectsCount(); + m_activePrim = m_scene.SceneGraph.GetActiveObjectsCount(); + m_activeScripts = m_scene.SceneGraph.GetActiveScriptsCount(); + + // FIXME: Checking for stat sanity is a complex approach. What we really need to do is fix the code + // so that stat numbers are always consistent. + CheckStatSanity(); // other MS is actually simulation time // m_otherMS = m_frameMS - m_physicsMS - m_imageMS - m_netMS - m_agentMS; @@ -462,13 +476,6 @@ namespace OpenSim.Region.Framework.Scenes m_timeDilation = td; } - public void SetRootAgents(int rootAgents) - { - m_rootAgents = rootAgents; - CheckStatSanity(); - - } - internal void CheckStatSanity() { if (m_rootAgents < 0 || m_childAgents < 0) @@ -485,22 +492,6 @@ namespace OpenSim.Region.Framework.Scenes } } - public void SetChildAgents(int childAgents) - { - m_childAgents = childAgents; - CheckStatSanity(); - } - - public void SetObjects(int objects) - { - m_numPrim = objects; - } - - public void SetActiveObjects(int objects) - { - m_activePrim = objects; - } - public void AddFPS(int frames) { m_fps += frames; @@ -587,11 +578,6 @@ namespace OpenSim.Region.Framework.Scenes m_scriptLinesPerSecond += count; } - public void SetActiveScripts(int count) - { - m_activeScripts = count; - } - public void AddPacketsStats(int inPackets, int outPackets, int unAckedBytes) { AddInPackets(inPackets); diff --git a/OpenSim/Region/Physics/OdePlugin/OdeScene.cs b/OpenSim/Region/Physics/OdePlugin/OdeScene.cs index fa659453c1..c6ecc68ae9 100644 --- a/OpenSim/Region/Physics/OdePlugin/OdeScene.cs +++ b/OpenSim/Region/Physics/OdePlugin/OdeScene.cs @@ -30,20 +30,21 @@ using System; using System.Collections.Generic; +using System.Diagnostics; +using System.IO; +using System.Linq; using System.Reflection; using System.Runtime.InteropServices; using System.Threading; -using System.IO; -using System.Diagnostics; using log4net; using Nini.Config; using Ode.NET; +using OpenMetaverse; #if USE_DRAWSTUFF using Drawstuff.NET; #endif using OpenSim.Framework; using OpenSim.Region.Physics.Manager; -using OpenMetaverse; namespace OpenSim.Region.Physics.OdePlugin { @@ -54,15 +55,15 @@ namespace OpenSim.Region.Physics.OdePlugin End = 2 } - public struct sCollisionData - { - public uint ColliderLocalId; - public uint CollidedWithLocalId; - public int NumberOfCollisions; - public int CollisionType; - public int StatusIndicator; - public int lastframe; - } +// public struct sCollisionData +// { +// public uint ColliderLocalId; +// public uint CollidedWithLocalId; +// public int NumberOfCollisions; +// public int CollisionType; +// public int StatusIndicator; +// public int lastframe; +// } [Flags] public enum CollisionCategories : int @@ -142,20 +143,114 @@ namespace OpenSim.Region.Physics.OdePlugin private Dictionary m_stats = new Dictionary(); /// - /// Stat name for recording the number of milliseconds that ODE spends in native collision code. + /// Stat name for total number of avatars in this ODE scene. /// - public const string ODENativeCollisionFrameMsStatName = "ODENativeCollisionFrameMS"; + public const string ODETotalAvatarsStatName = "ODETotalAvatars"; + + /// + /// Stat name for total number of prims in this ODE scene. + /// + public const string ODETotalPrimsStatName = "ODETotalPrims"; + + /// + /// Stat name for total number of prims with active physics in this ODE scene. + /// + public const string ODEActivePrimsStatName = "ODEActivePrims"; + + /// + /// Stat name for the total time spent in ODE frame processing. + /// + /// + /// A sanity check for the main scene loop physics time. + /// + public const string ODETotalFrameMsStatName = "ODETotalFrameMS"; + + /// + /// Stat name for time spent processing avatar taints per frame + /// + public const string ODEAvatarTaintMsStatName = "ODEAvatarTaintFrameMS"; + + /// + /// Stat name for time spent processing prim taints per frame + /// + public const string ODEPrimTaintMsStatName = "ODEPrimTaintFrameMS"; + + /// + /// Stat name for time spent calculating avatar forces per frame. + /// + public const string ODEAvatarForcesFrameMsStatName = "ODEAvatarForcesFrameMS"; + + /// + /// Stat name for time spent calculating prim forces per frame + /// + public const string ODEPrimForcesFrameMsStatName = "ODEPrimForcesFrameMS"; + + /// + /// Stat name for time spent fulfilling raycasting requests per frame + /// + public const string ODERaycastingFrameMsStatName = "ODERaycastingFrameMS"; + + /// + /// Stat name for time spent in native code that actually steps through the simulation. + /// + public const string ODENativeStepFrameMsStatName = "ODENativeStepFrameMS"; + + /// + /// Stat name for the number of milliseconds that ODE spends in native space collision code. + /// + public const string ODENativeSpaceCollisionFrameMsStatName = "ODENativeSpaceCollisionFrameMS"; + + /// + /// Stat name for milliseconds that ODE spends in native geom collision code. + /// + public const string ODENativeGeomCollisionFrameMsStatName = "ODENativeGeomCollisionFrameMS"; + + /// + /// Time spent in collision processing that is not spent in native space or geom collision code. + /// + public const string ODEOtherCollisionFrameMsStatName = "ODEOtherCollisionFrameMS"; + + /// + /// Stat name for time spent notifying listeners of collisions + /// + public const string ODECollisionNotificationFrameMsStatName = "ODECollisionNotificationFrameMS"; + + /// + /// Stat name for milliseconds spent updating avatar position and velocity + /// + public const string ODEAvatarUpdateFrameMsStatName = "ODEAvatarUpdateFrameMS"; + + /// + /// Stat name for the milliseconds spent updating prim position and velocity + /// + public const string ODEPrimUpdateFrameMsStatName = "ODEPrimUpdateFrameMS"; + + /// + /// Stat name for avatar collisions with another entity. + /// + public const string ODEAvatarContactsStatsName = "ODEAvatarContacts"; + + /// + /// Stat name for prim collisions with another entity. + /// + public const string ODEPrimContactsStatName = "ODEPrimContacts"; /// /// Used to hold tick numbers for stat collection purposes. /// - private int m_nativeCollisionTickRecorder; + private int m_nativeCollisionStartTick; /// /// A messy way to tell if we need to avoid adding a collision time because this was already done in the callback. /// private bool m_inCollisionTiming; + /// + /// A temporary holder for the number of avatar collisions in a frame, so we can work out how many object + /// collisions occured using the _perloopcontact if stats collection is enabled. + /// + private int m_tempAvatarCollisionsThisFrame; + /// /// Used in calculating physics frame time dilation /// @@ -472,7 +567,7 @@ namespace OpenSim.Region.Physics.OdePlugin // Initialize the mesh plugin public override void Initialise(IMesher meshmerizer, IConfigSource config) { - m_stats[ODENativeCollisionFrameMsStatName] = 0; + InitializeExtraStats(); mesher = meshmerizer; m_config = config; @@ -818,7 +913,7 @@ namespace OpenSim.Region.Physics.OdePlugin { // We do this inside the lock so that we don't count any delay in acquiring it if (CollectStats) - m_nativeCollisionTickRecorder = Util.EnvironmentTickCount(); + m_nativeCollisionStartTick = Util.EnvironmentTickCount(); count = d.Collide(geom1, geom2, maxContacts, contactsArray, contactGeomSize); } @@ -826,8 +921,8 @@ namespace OpenSim.Region.Physics.OdePlugin // We do this outside the lock so that any waiting threads aren't held up, though the effect is probably // negligable if (CollectStats) - m_stats[ODENativeCollisionFrameMsStatName] - += Util.EnvironmentTickCountSubtract(m_nativeCollisionTickRecorder); + m_stats[ODENativeGeomCollisionFrameMsStatName] + += Util.EnvironmentTickCountSubtract(m_nativeCollisionStartTick); return count; } @@ -843,15 +938,15 @@ namespace OpenSim.Region.Physics.OdePlugin if (CollectStats) { m_inCollisionTiming = true; - m_nativeCollisionTickRecorder = Util.EnvironmentTickCount(); + m_nativeCollisionStartTick = Util.EnvironmentTickCount(); } d.SpaceCollide2(space1, space2, data, nearCallback); if (CollectStats && m_inCollisionTiming) { - m_stats[ODENativeCollisionFrameMsStatName] - += Util.EnvironmentTickCountSubtract(m_nativeCollisionTickRecorder); + m_stats[ODENativeSpaceCollisionFrameMsStatName] + += Util.EnvironmentTickCountSubtract(m_nativeCollisionStartTick); m_inCollisionTiming = false; } } @@ -866,8 +961,8 @@ namespace OpenSim.Region.Physics.OdePlugin { if (CollectStats && m_inCollisionTiming) { - m_stats[ODENativeCollisionFrameMsStatName] - += Util.EnvironmentTickCountSubtract(m_nativeCollisionTickRecorder); + m_stats[ODENativeSpaceCollisionFrameMsStatName] + += Util.EnvironmentTickCountSubtract(m_nativeCollisionStartTick); m_inCollisionTiming = false; } @@ -945,6 +1040,10 @@ namespace OpenSim.Region.Physics.OdePlugin count = CollideGeoms(g1, g2, contacts.Length, contacts, d.ContactGeom.SizeOf); + // All code after this is only relevant if we have any collisions + if (count <= 0) + return; + if (count > contacts.Length) m_log.Error("[ODE SCENE]: Got " + count + " contacts when we asked for a maximum of " + contacts.Length); } @@ -1212,14 +1311,12 @@ namespace OpenSim.Region.Physics.OdePlugin { _perloopContact.Add(curContact); - // If we're colliding against terrain if (name1 == "Terrain" || name2 == "Terrain") { - // If we're moving if ((p2.PhysicsActorType == (int) ActorTypes.Agent) && (Math.Abs(p2.Velocity.X) > 0.01f || Math.Abs(p2.Velocity.Y) > 0.01f)) { - // Use the movement terrain contact + // Avatar is moving on terrain, use the movement terrain contact AvatarMovementTerrainContact.geom = curContact; if (m_global_contactcount < maxContactsbeforedeath) @@ -1232,7 +1329,7 @@ namespace OpenSim.Region.Physics.OdePlugin { if (p2.PhysicsActorType == (int)ActorTypes.Agent) { - // Use the non moving terrain contact + // Avatar is standing on terrain, use the non moving terrain contact TerrainContact.geom = curContact; if (m_global_contactcount < maxContactsbeforedeath) @@ -1327,13 +1424,11 @@ namespace OpenSim.Region.Physics.OdePlugin } else { - // we're colliding with prim or avatar - // check if we're moving if ((p2.PhysicsActorType == (int)ActorTypes.Agent)) { if ((Math.Abs(p2.Velocity.X) > 0.01f || Math.Abs(p2.Velocity.Y) > 0.01f)) { - // Use the Movement prim contact + // Avatar is moving on a prim, use the Movement prim contact AvatarMovementprimContact.geom = curContact; if (m_global_contactcount < maxContactsbeforedeath) @@ -1344,9 +1439,8 @@ namespace OpenSim.Region.Physics.OdePlugin } else { - // Use the non movement contact + // Avatar is standing still on a prim, use the non movement contact contact.geom = curContact; - _perloopContact.Add(curContact); if (m_global_contactcount < maxContactsbeforedeath) { @@ -1454,7 +1548,7 @@ namespace OpenSim.Region.Physics.OdePlugin break; } } - //m_log.DebugFormat("[Collsion]: Depth {0}", Math.Abs(contact.depth - contactGeom.depth)); + //m_log.DebugFormat("[Collision]: Depth {0}", Math.Abs(contact.depth - contactGeom.depth)); //m_log.DebugFormat("[Collision]: <{0},{1},{2}>", Math.Abs(contactGeom.normal.X - contact.normal.X), Math.Abs(contactGeom.normal.Y - contact.normal.Y), Math.Abs(contactGeom.normal.Z - contact.normal.Z)); } } @@ -1692,8 +1786,11 @@ namespace OpenSim.Region.Physics.OdePlugin //} } -// if (framecount % 55 == 0) -// m_log.DebugFormat("Processed {0} collisions", _perloopContact.Count); + if (CollectStats) + { + m_tempAvatarCollisionsThisFrame = _perloopContact.Count; + m_stats[ODEAvatarContactsStatsName] += m_tempAvatarCollisionsThisFrame; + } List removeprims = null; foreach (OdePrim chr in _activeprims) @@ -1727,6 +1824,9 @@ namespace OpenSim.Region.Physics.OdePlugin } } + if (CollectStats) + m_stats[ODEPrimContactsStatName] += _perloopContact.Count - m_tempAvatarCollisionsThisFrame; + if (removeprims != null) { foreach (OdePrim chr in removeprims) @@ -2785,21 +2885,23 @@ namespace OpenSim.Region.Physics.OdePlugin /// /// This is our main simulate loop + /// + /// /// It's thread locked by a Mutex in the scene. /// It holds Collisions, it instructs ODE to step through the physical reactions /// It moves the objects around in memory /// It calls the methods that report back to the object owners.. (scenepresence, SceneObjectGroup) - /// + /// /// /// The number of frames simulated over that period. public override float Simulate(float timeStep) { + int startFrameTick = CollectStats ? Util.EnvironmentTickCount() : 0; + int tempTick = 0, tempTick2 = 0; + if (framecount >= int.MaxValue) framecount = 0; - //if (m_worldOffset != Vector3.Zero) - // return 0; - framecount++; float fps = 0; @@ -2807,7 +2909,7 @@ namespace OpenSim.Region.Physics.OdePlugin float timeLeft = timeStep; //m_log.Info(timeStep.ToString()); -// step_time += timeStep; +// step_time += timeSte // // // If We're loaded down by something else, // // or debugging with the Visual Studio project on pause @@ -2873,6 +2975,9 @@ namespace OpenSim.Region.Physics.OdePlugin { try { + if (CollectStats) + tempTick = Util.EnvironmentTickCount(); + lock (_taintedActors) { foreach (OdeCharacter character in _taintedActors) @@ -2881,6 +2986,13 @@ namespace OpenSim.Region.Physics.OdePlugin _taintedActors.Clear(); } + if (CollectStats) + { + tempTick2 = Util.EnvironmentTickCount(); + m_stats[ODEAvatarTaintMsStatName] += Util.EnvironmentTickCountSubtract(tempTick2, tempTick); + tempTick = tempTick2; + } + lock (_taintedPrims) { foreach (OdePrim prim in _taintedPrims) @@ -2911,6 +3023,13 @@ namespace OpenSim.Region.Physics.OdePlugin _taintedPrims.Clear(); } + if (CollectStats) + { + tempTick2 = Util.EnvironmentTickCount(); + m_stats[ODEPrimTaintMsStatName] += Util.EnvironmentTickCountSubtract(tempTick2, tempTick); + tempTick = tempTick2; + } + // Move characters foreach (OdeCharacter actor in _characters) actor.Move(defects); @@ -2930,6 +3049,13 @@ namespace OpenSim.Region.Physics.OdePlugin defects.Clear(); } + if (CollectStats) + { + tempTick2 = Util.EnvironmentTickCount(); + m_stats[ODEAvatarForcesFrameMsStatName] += Util.EnvironmentTickCountSubtract(tempTick2, tempTick); + tempTick = tempTick2; + } + // Move other active objects foreach (OdePrim prim in _activeprims) { @@ -2937,14 +3063,35 @@ namespace OpenSim.Region.Physics.OdePlugin prim.Move(timeStep); } + if (CollectStats) + { + tempTick2 = Util.EnvironmentTickCount(); + m_stats[ODEPrimForcesFrameMsStatName] += Util.EnvironmentTickCountSubtract(tempTick2, tempTick); + tempTick = tempTick2; + } + //if ((framecount % m_randomizeWater) == 0) // randomizeWater(waterlevel); //int RayCastTimeMS = m_rayCastManager.ProcessQueuedRequests(); m_rayCastManager.ProcessQueuedRequests(); + if (CollectStats) + { + tempTick2 = Util.EnvironmentTickCount(); + m_stats[ODERaycastingFrameMsStatName] += Util.EnvironmentTickCountSubtract(tempTick2, tempTick); + tempTick = tempTick2; + } + collision_optimized(); + if (CollectStats) + { + tempTick2 = Util.EnvironmentTickCount(); + m_stats[ODEOtherCollisionFrameMsStatName] += Util.EnvironmentTickCountSubtract(tempTick2, tempTick); + tempTick = tempTick2; + } + foreach (PhysicsActor obj in _collisionEventPrim.Values) { // m_log.DebugFormat("[PHYSICS]: Assessing {0} {1} for collision events", obj.SOPName, obj.LocalID); @@ -2969,9 +3116,19 @@ namespace OpenSim.Region.Physics.OdePlugin // "[PHYSICS]: Collision contacts to process this frame = {0}", m_global_contactcount); m_global_contactcount = 0; - + + if (CollectStats) + { + tempTick2 = Util.EnvironmentTickCount(); + m_stats[ODECollisionNotificationFrameMsStatName] += Util.EnvironmentTickCountSubtract(tempTick2, tempTick); + tempTick = tempTick2; + } + d.WorldQuickStep(world, ODE_STEPSIZE); + if (CollectStats) + m_stats[ODENativeStepFrameMsStatName] += Util.EnvironmentTickCountSubtract(tempTick); + d.JointGroupEmpty(contactgroup); } catch (Exception e) @@ -2982,6 +3139,9 @@ namespace OpenSim.Region.Physics.OdePlugin timeLeft -= ODE_STEPSIZE; } + if (CollectStats) + tempTick = Util.EnvironmentTickCount(); + foreach (OdeCharacter actor in _characters) { if (actor.bad) @@ -3005,6 +3165,13 @@ namespace OpenSim.Region.Physics.OdePlugin defects.Clear(); } + if (CollectStats) + { + tempTick2 = Util.EnvironmentTickCount(); + m_stats[ODEAvatarUpdateFrameMsStatName] += Util.EnvironmentTickCountSubtract(tempTick2, tempTick); + tempTick = tempTick2; + } + //if (timeStep < 0.2f) foreach (OdePrim prim in _activeprims) @@ -3018,6 +3185,9 @@ namespace OpenSim.Region.Physics.OdePlugin } } + if (CollectStats) + m_stats[ODEPrimUpdateFrameMsStatName] += Util.EnvironmentTickCountSubtract(tempTick); + //DumpJointInfo(); // Finished with all sim stepping. If requested, dump world state to file for debugging. @@ -3039,7 +3209,7 @@ namespace OpenSim.Region.Physics.OdePlugin d.WorldExportDIF(world, fname, physics_logging_append_existing_logfile, prefix); } - latertickcount = Util.EnvironmentTickCount() - tickCountFrameRun; + latertickcount = Util.EnvironmentTickCountSubtract(tickCountFrameRun); // OpenSimulator above does 10 fps. 10 fps = means that the main thread loop and physics // has a max of 100 ms to run theoretically. @@ -3059,6 +3229,9 @@ namespace OpenSim.Region.Physics.OdePlugin tickCountFrameRun = Util.EnvironmentTickCount(); } + if (CollectStats) + m_stats[ODETotalFrameMsStatName] += Util.EnvironmentTickCountSubtract(startFrameTick); + return fps; } @@ -3868,26 +4041,19 @@ namespace OpenSim.Region.Physics.OdePlugin public override Dictionary GetTopColliders() { - Dictionary returncolliders = new Dictionary(); - int cnt = 0; + Dictionary topColliders; + lock (_prims) { - foreach (OdePrim prm in _prims) - { - if (prm.CollisionScore > 0) - { - returncolliders.Add(prm.LocalID, prm.CollisionScore); - cnt++; - prm.CollisionScore = 0f; - if (cnt > 25) - { - break; - } - } - } + List orderedPrims = new List(_prims); + orderedPrims.OrderByDescending(p => p.CollisionScore).Take(25); + topColliders = orderedPrims.ToDictionary(p => p.LocalID, p => p.CollisionScore); + + foreach (OdePrim p in _prims) + p.CollisionScore = 0; } - return returncolliders; + return topColliders; } public override bool SupportsRayCast() @@ -4069,10 +4235,40 @@ namespace OpenSim.Region.Physics.OdePlugin { returnStats = new Dictionary(m_stats); - m_stats[ODENativeCollisionFrameMsStatName] = 0; + // FIXME: This is a SUPER DUMB HACK until we can establish stats that aren't subject to a division by + // 3 from the SimStatsReporter. + returnStats[ODETotalAvatarsStatName] = _characters.Count * 3; + returnStats[ODETotalPrimsStatName] = _prims.Count * 3; + returnStats[ODEActivePrimsStatName] = _activeprims.Count * 3; + + InitializeExtraStats(); } + returnStats[ODEOtherCollisionFrameMsStatName] + = returnStats[ODEOtherCollisionFrameMsStatName] + - returnStats[ODENativeSpaceCollisionFrameMsStatName] + - returnStats[ODENativeGeomCollisionFrameMsStatName]; + return returnStats; } + + private void InitializeExtraStats() + { + m_stats[ODETotalFrameMsStatName] = 0; + m_stats[ODEAvatarTaintMsStatName] = 0; + m_stats[ODEPrimTaintMsStatName] = 0; + m_stats[ODEAvatarForcesFrameMsStatName] = 0; + m_stats[ODEPrimForcesFrameMsStatName] = 0; + m_stats[ODERaycastingFrameMsStatName] = 0; + m_stats[ODENativeStepFrameMsStatName] = 0; + m_stats[ODENativeSpaceCollisionFrameMsStatName] = 0; + m_stats[ODENativeGeomCollisionFrameMsStatName] = 0; + m_stats[ODEOtherCollisionFrameMsStatName] = 0; + m_stats[ODECollisionNotificationFrameMsStatName] = 0; + m_stats[ODEAvatarContactsStatsName] = 0; + m_stats[ODEPrimContactsStatName] = 0; + m_stats[ODEAvatarUpdateFrameMsStatName] = 0; + m_stats[ODEPrimUpdateFrameMsStatName] = 0; + } } } \ No newline at end of file