From bf0b8170f7617fe9524b691b59ca7a7b660aec30 Mon Sep 17 00:00:00 2001 From: "Justin Clark-Casey (justincc)" Date: Tue, 29 May 2012 23:35:20 +0100 Subject: [PATCH 01/10] Add console command "teleport user" to allow teleport from the region console See "help teleport user" on the console for more details --- .../Avatar/Commands/UserCommandsModule.cs | 189 ++++++++++++++++++ 1 file changed, 189 insertions(+) create mode 100644 OpenSim/Region/CoreModules/Avatar/Commands/UserCommandsModule.cs diff --git a/OpenSim/Region/CoreModules/Avatar/Commands/UserCommandsModule.cs b/OpenSim/Region/CoreModules/Avatar/Commands/UserCommandsModule.cs new file mode 100644 index 0000000000..4bcd2ac499 --- /dev/null +++ b/OpenSim/Region/CoreModules/Avatar/Commands/UserCommandsModule.cs @@ -0,0 +1,189 @@ +/* + * Copyright (c) Contributors, http://opensimulator.org/ + * See CONTRIBUTORS.TXT for a full list of copyright holders. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * * Neither the name of the OpenSimulator Project nor the + * names of its contributors may be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE DEVELOPERS ``AS IS'' AND ANY + * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE CONTRIBUTORS BE LIABLE FOR ANY + * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +using System; +using System.Collections.Generic; +using System.Reflection; +using System.Text; +using System.Text.RegularExpressions; +using log4net; +using Mono.Addins; +using NDesk.Options; +using Nini.Config; +using OpenMetaverse; +using OpenSim.Framework; +using OpenSim.Framework.Console; +using OpenSim.Framework.Statistics; +using OpenSim.Region.Framework.Interfaces; +using OpenSim.Region.Framework.Scenes; + +namespace OpenSim.Region.CoreModules.Avatars.Commands +{ + /// + /// A module that holds commands for manipulating objects in the scene. + /// + [Extension(Path = "/OpenSim/RegionModules", NodeName = "RegionModule", Id = "UserCommandsModule")] + public class UserCommandsModule : ISharedRegionModule + { +// private static readonly ILog m_log = LogManager.GetLogger(MethodBase.GetCurrentMethod().DeclaringType); + + public const string TeleportUserCommandSyntax = "teleport user "; + + public static Regex InterRegionDestinationRegex + = new Regex(@"^(?.+)/(?\d+)/(?\d+)/(?\d+)$", RegexOptions.Compiled); + + public static Regex WithinRegionDestinationRegex + = new Regex(@"^(?\d+)/(?\d+)/(?\d+)$", RegexOptions.Compiled); + + private Dictionary m_scenes = new Dictionary(); + + public string Name { get { return "User Commands Module"; } } + + public Type ReplaceableInterface { get { return null; } } + + public void Initialise(IConfigSource source) + { +// m_log.DebugFormat("[USER COMMANDS MODULE]: INITIALIZED MODULE"); + } + + public void PostInitialise() + { +// m_log.DebugFormat("[USER COMMANDS MODULE]: POST INITIALIZED MODULE"); + } + + public void Close() + { +// m_log.DebugFormat("[USER COMMANDS MODULE]: CLOSED MODULE"); + } + + public void AddRegion(Scene scene) + { +// m_log.DebugFormat("[USER COMMANDS MODULE]: REGION {0} ADDED", scene.RegionInfo.RegionName); + + lock (m_scenes) + m_scenes[scene.RegionInfo.RegionID] = scene; + + scene.AddCommand( + "Users", + this, + "teleport user", + TeleportUserCommandSyntax, + "Teleport a user in this simulator to the given destination", + " is in format []///, e.g. regionone/20/30/40 or just 20/30/40 to teleport within same region." + + "\nIf the region contains a space then the whole destination must be in quotes, e.g. \"region one/20/30/40\"", + HandleTeleportUser); + } + + public void RemoveRegion(Scene scene) + { +// m_log.DebugFormat("[USER COMMANDS MODULE]: REGION {0} REMOVED", scene.RegionInfo.RegionName); + + lock (m_scenes) + m_scenes.Remove(scene.RegionInfo.RegionID); + } + + public void RegionLoaded(Scene scene) + { +// m_log.DebugFormat("[USER COMMANDS MODULE]: REGION {0} LOADED", scene.RegionInfo.RegionName); + } + + private ScenePresence GetUser(string firstName, string lastName) + { + ScenePresence userFound = null; + + lock (m_scenes) + { + foreach (Scene scene in m_scenes.Values) + { + ScenePresence user = scene.GetScenePresence(firstName, lastName); + if (user != null && !user.IsChildAgent) + { + userFound = user; + break; + } + } + } + + return userFound; + } + + private void HandleTeleportUser(string module, string[] cmd) + { + if (cmd.Length < 5) + { + MainConsole.Instance.OutputFormat("Usage: " + TeleportUserCommandSyntax); + return; + } + + string firstName = cmd[2]; + string lastName = cmd[3]; + string rawDestination = cmd[4]; + + ScenePresence user = GetUser(firstName, lastName); + + if (user == null) + { + MainConsole.Instance.OutputFormat("No user found with name {0} {1}", firstName, lastName); + return; + } + +// MainConsole.Instance.OutputFormat("rawDestination [{0}]", rawDestination); + + Match m = WithinRegionDestinationRegex.Match(rawDestination); + + if (!m.Success) + { + m = InterRegionDestinationRegex.Match(rawDestination); + + if (!m.Success) + { + MainConsole.Instance.OutputFormat("Invalid destination {0}", rawDestination); + return; + } + } + + string regionName + = m.Groups["regionName"].Success ? m.Groups["regionName"].Value : user.Scene.RegionInfo.RegionName; + + MainConsole.Instance.OutputFormat( + "Teleporting {0} to {1},{2},{3} in {4}", + user.Name, + m.Groups["x"], m.Groups["y"], m.Groups["z"], + regionName); + + user.Scene.RequestTeleportLocation( + user.ControllingClient, + regionName, + new Vector3( + float.Parse(m.Groups["x"].Value), + float.Parse(m.Groups["y"].Value), + float.Parse(m.Groups["z"].Value)), + user.Lookat, + (uint)TeleportFlags.ViaLocation); + } + } +} \ No newline at end of file From 0b02a4d42e989609a4e1ba39d2aee9a7f9655613 Mon Sep 17 00:00:00 2001 From: "Justin Clark-Casey (justincc)" Date: Thu, 31 May 2012 01:52:26 +0100 Subject: [PATCH 02/10] Add an optional mechanism for physics modules to collect and return arbitrary stats. If active, the physics module can return arbitrary stat counters that can be seen via the MonitoringModule (http://opensimulator.org/wiki/Monitoring_Module) This is only active in OdeScene if collect_stats = true in [ODEPhysicsSettings]. This patch allows OdeScene to collect elapsed time information for calls to the ODE native collision methods to assess what proportion of time this takes compared to total physics processing. This data is returned as ODENativeCollisionFrameMS in the monitoring module, updated every 3 seconds. The performance effect of collecting stats is probably extremely minor, dwarfed by the rest of the physics code. --- OpenSim/Region/Application/OpenSim.cs | 3 +- .../Framework/Monitoring/MonitorModule.cs | 108 ++++++++------ .../Framework/Scenes/SimStatsReporter.cs | 48 +++++- .../Region/Physics/Manager/PhysicsScene.cs | 14 ++ OpenSim/Region/Physics/OdePlugin/OdeScene.cs | 139 ++++++++++++++++-- bin/OpenSimDefaults.ini | 30 ++-- 6 files changed, 271 insertions(+), 71 deletions(-) diff --git a/OpenSim/Region/Application/OpenSim.cs b/OpenSim/Region/Application/OpenSim.cs index 6796f2bd33..c0913c5296 100644 --- a/OpenSim/Region/Application/OpenSim.cs +++ b/OpenSim/Region/Application/OpenSim.cs @@ -28,6 +28,7 @@ using System; using System.Collections; using System.Collections.Generic; +using System.Diagnostics; using System.IO; using System.Reflection; using System.Text; @@ -138,7 +139,7 @@ namespace OpenSim m_log.Info("===================================================================="); m_log.Info("========================= STARTING OPENSIM ========================="); m_log.Info("===================================================================="); - + //m_log.InfoFormat("[OPENSIM MAIN]: GC Is Server GC: {0}", GCSettings.IsServerGC.ToString()); // http://msdn.microsoft.com/en-us/library/bb384202.aspx //GCSettings.LatencyMode = GCLatencyMode.Batch; diff --git a/OpenSim/Region/CoreModules/Framework/Monitoring/MonitorModule.cs b/OpenSim/Region/CoreModules/Framework/Monitoring/MonitorModule.cs index 7f8271d401..4a8c369335 100644 --- a/OpenSim/Region/CoreModules/Framework/Monitoring/MonitorModule.cs +++ b/OpenSim/Region/CoreModules/Framework/Monitoring/MonitorModule.cs @@ -49,7 +49,16 @@ namespace OpenSim.Region.CoreModules.Framework.Monitoring public bool Enabled { get; private set; } private Scene m_scene; - private readonly List m_monitors = new List(); + + /// + /// These are monitors where we know the static details in advance. + /// + /// + /// Dynamic monitors also exist (we don't know any of the details of what stats we get back here) + /// but these are currently hardcoded. + /// + private readonly List m_staticMonitors = new List(); + private readonly List m_alerts = new List(); private static readonly ILog m_log = LogManager.GetLogger(MethodBase.GetCurrentMethod().DeclaringType); @@ -84,9 +93,18 @@ namespace OpenSim.Region.CoreModules.Framework.Monitoring public void DebugMonitors(string module, string[] args) { - foreach (IMonitor monitor in m_monitors) + foreach (IMonitor monitor in m_staticMonitors) { - m_log.Info("[MonitorModule]: " + m_scene.RegionInfo.RegionName + " reports " + monitor.GetFriendlyName() + " = " + monitor.GetFriendlyValue()); + m_log.InfoFormat( + "[MONITOR MODULE]: {0} reports {1} = {2}", + m_scene.RegionInfo.RegionName, monitor.GetFriendlyName(), monitor.GetFriendlyValue()); + } + + foreach (KeyValuePair tuple in m_scene.StatsReporter.GetExtraSimStats()) + { + m_log.InfoFormat( + "[MONITOR MODULE]: {0} reports {1} = {2}", + m_scene.RegionInfo.RegionName, tuple.Key, tuple.Value); } } @@ -106,11 +124,12 @@ namespace OpenSim.Region.CoreModules.Framework.Monitoring { string monID = (string) request["monitor"]; - foreach (IMonitor monitor in m_monitors) + foreach (IMonitor monitor in m_staticMonitors) { string elemName = monitor.ToString(); if (elemName.StartsWith(monitor.GetType().Namespace)) elemName = elemName.Substring(monitor.GetType().Namespace.Length + 1); + if (elemName == monID || monitor.ToString() == monID) { Hashtable ereply3 = new Hashtable(); @@ -123,6 +142,9 @@ namespace OpenSim.Region.CoreModules.Framework.Monitoring } } + // FIXME: Arguably this should also be done with dynamic monitors but I'm not sure what the above code + // is even doing. Why are we inspecting the type of the monitor??? + // No monitor with that name Hashtable ereply2 = new Hashtable(); @@ -134,12 +156,18 @@ namespace OpenSim.Region.CoreModules.Framework.Monitoring } string xml = ""; - foreach (IMonitor monitor in m_monitors) + foreach (IMonitor monitor in m_staticMonitors) { string elemName = monitor.GetName(); xml += "<" + elemName + ">" + monitor.GetValue().ToString() + ""; // m_log.DebugFormat("[MONITOR MODULE]: {0} = {1}", elemName, monitor.GetValue()); } + + foreach (KeyValuePair tuple in m_scene.StatsReporter.GetExtraSimStats()) + { + xml += "<" + tuple.Key + ">" + tuple.Value + ""; + } + xml += ""; Hashtable ereply = new Hashtable(); @@ -156,20 +184,20 @@ namespace OpenSim.Region.CoreModules.Framework.Monitoring if (!Enabled) return; - m_monitors.Add(new AgentCountMonitor(m_scene)); - m_monitors.Add(new ChildAgentCountMonitor(m_scene)); - m_monitors.Add(new GCMemoryMonitor()); - m_monitors.Add(new ObjectCountMonitor(m_scene)); - m_monitors.Add(new PhysicsFrameMonitor(m_scene)); - m_monitors.Add(new PhysicsUpdateFrameMonitor(m_scene)); - m_monitors.Add(new PWSMemoryMonitor()); - m_monitors.Add(new ThreadCountMonitor()); - m_monitors.Add(new TotalFrameMonitor(m_scene)); - m_monitors.Add(new EventFrameMonitor(m_scene)); - m_monitors.Add(new LandFrameMonitor(m_scene)); - m_monitors.Add(new LastFrameTimeMonitor(m_scene)); + m_staticMonitors.Add(new AgentCountMonitor(m_scene)); + m_staticMonitors.Add(new ChildAgentCountMonitor(m_scene)); + m_staticMonitors.Add(new GCMemoryMonitor()); + m_staticMonitors.Add(new ObjectCountMonitor(m_scene)); + m_staticMonitors.Add(new PhysicsFrameMonitor(m_scene)); + m_staticMonitors.Add(new PhysicsUpdateFrameMonitor(m_scene)); + m_staticMonitors.Add(new PWSMemoryMonitor()); + m_staticMonitors.Add(new ThreadCountMonitor()); + m_staticMonitors.Add(new TotalFrameMonitor(m_scene)); + m_staticMonitors.Add(new EventFrameMonitor(m_scene)); + m_staticMonitors.Add(new LandFrameMonitor(m_scene)); + m_staticMonitors.Add(new LastFrameTimeMonitor(m_scene)); - m_monitors.Add( + m_staticMonitors.Add( new GenericMonitor( m_scene, "TimeDilationMonitor", @@ -177,7 +205,7 @@ namespace OpenSim.Region.CoreModules.Framework.Monitoring m => m.Scene.StatsReporter.LastReportedSimStats[0], m => m.GetValue().ToString())); - m_monitors.Add( + m_staticMonitors.Add( new GenericMonitor( m_scene, "SimFPSMonitor", @@ -185,7 +213,7 @@ namespace OpenSim.Region.CoreModules.Framework.Monitoring m => m.Scene.StatsReporter.LastReportedSimStats[1], m => string.Format("{0}", m.GetValue()))); - m_monitors.Add( + m_staticMonitors.Add( new GenericMonitor( m_scene, "PhysicsFPSMonitor", @@ -193,7 +221,7 @@ namespace OpenSim.Region.CoreModules.Framework.Monitoring m => m.Scene.StatsReporter.LastReportedSimStats[2], m => string.Format("{0}", m.GetValue()))); - m_monitors.Add( + m_staticMonitors.Add( new GenericMonitor( m_scene, "AgentUpdatesPerSecondMonitor", @@ -201,15 +229,7 @@ namespace OpenSim.Region.CoreModules.Framework.Monitoring m => m.Scene.StatsReporter.LastReportedSimStats[3], m => string.Format("{0} per second", m.GetValue()))); - m_monitors.Add( - new GenericMonitor( - m_scene, - "ObjectUpdatesPerSecondMonitor", - "Object Updates", - m => m.Scene.StatsReporter.LastReportedObjectUpdates, - m => string.Format("{0} per second", m.GetValue()))); - - m_monitors.Add( + m_staticMonitors.Add( new GenericMonitor( m_scene, "ActiveObjectCountMonitor", @@ -217,7 +237,7 @@ namespace OpenSim.Region.CoreModules.Framework.Monitoring m => m.Scene.StatsReporter.LastReportedSimStats[7], m => string.Format("{0}", m.GetValue()))); - m_monitors.Add( + m_staticMonitors.Add( new GenericMonitor( m_scene, "ActiveScriptsMonitor", @@ -225,7 +245,7 @@ namespace OpenSim.Region.CoreModules.Framework.Monitoring m => m.Scene.StatsReporter.LastReportedSimStats[19], m => string.Format("{0}", m.GetValue()))); - m_monitors.Add( + m_staticMonitors.Add( new GenericMonitor( m_scene, "ScriptEventsPerSecondMonitor", @@ -233,7 +253,7 @@ namespace OpenSim.Region.CoreModules.Framework.Monitoring m => m.Scene.StatsReporter.LastReportedSimStats[20], m => string.Format("{0} per second", m.GetValue()))); - m_monitors.Add( + m_staticMonitors.Add( new GenericMonitor( m_scene, "InPacketsPerSecondMonitor", @@ -241,7 +261,7 @@ namespace OpenSim.Region.CoreModules.Framework.Monitoring m => m.Scene.StatsReporter.LastReportedSimStats[13], m => string.Format("{0} per second", m.GetValue()))); - m_monitors.Add( + m_staticMonitors.Add( new GenericMonitor( m_scene, "OutPacketsPerSecondMonitor", @@ -249,7 +269,7 @@ namespace OpenSim.Region.CoreModules.Framework.Monitoring m => m.Scene.StatsReporter.LastReportedSimStats[14], m => string.Format("{0} per second", m.GetValue()))); - m_monitors.Add( + m_staticMonitors.Add( new GenericMonitor( m_scene, "UnackedBytesMonitor", @@ -257,7 +277,7 @@ namespace OpenSim.Region.CoreModules.Framework.Monitoring m => m.Scene.StatsReporter.LastReportedSimStats[15], m => string.Format("{0}", m.GetValue()))); - m_monitors.Add( + m_staticMonitors.Add( new GenericMonitor( m_scene, "PendingDownloadsMonitor", @@ -265,7 +285,7 @@ namespace OpenSim.Region.CoreModules.Framework.Monitoring m => m.Scene.StatsReporter.LastReportedSimStats[17], m => string.Format("{0}", m.GetValue()))); - m_monitors.Add( + m_staticMonitors.Add( new GenericMonitor( m_scene, "PendingUploadsMonitor", @@ -273,7 +293,7 @@ namespace OpenSim.Region.CoreModules.Framework.Monitoring m => m.Scene.StatsReporter.LastReportedSimStats[18], m => string.Format("{0}", m.GetValue()))); - m_monitors.Add( + m_staticMonitors.Add( new GenericMonitor( m_scene, "TotalFrameTimeMonitor", @@ -281,7 +301,7 @@ namespace OpenSim.Region.CoreModules.Framework.Monitoring m => m.Scene.StatsReporter.LastReportedSimStats[8], m => string.Format("{0} ms", m.GetValue()))); - m_monitors.Add( + m_staticMonitors.Add( new GenericMonitor( m_scene, "NetFrameTimeMonitor", @@ -289,7 +309,7 @@ namespace OpenSim.Region.CoreModules.Framework.Monitoring m => m.Scene.StatsReporter.LastReportedSimStats[9], m => string.Format("{0} ms", m.GetValue()))); - m_monitors.Add( + m_staticMonitors.Add( new GenericMonitor( m_scene, "PhysicsFrameTimeMonitor", @@ -297,7 +317,7 @@ namespace OpenSim.Region.CoreModules.Framework.Monitoring m => m.Scene.StatsReporter.LastReportedSimStats[10], m => string.Format("{0} ms", m.GetValue()))); - m_monitors.Add( + m_staticMonitors.Add( new GenericMonitor( m_scene, "SimulationFrameTimeMonitor", @@ -305,7 +325,7 @@ namespace OpenSim.Region.CoreModules.Framework.Monitoring m => m.Scene.StatsReporter.LastReportedSimStats[12], m => string.Format("{0} ms", m.GetValue()))); - m_monitors.Add( + m_staticMonitors.Add( new GenericMonitor( m_scene, "AgentFrameTimeMonitor", @@ -313,7 +333,7 @@ namespace OpenSim.Region.CoreModules.Framework.Monitoring m => m.Scene.StatsReporter.LastReportedSimStats[16], m => string.Format("{0} ms", m.GetValue()))); - m_monitors.Add( + m_staticMonitors.Add( new GenericMonitor( m_scene, "ImagesFrameTimeMonitor", @@ -321,7 +341,7 @@ namespace OpenSim.Region.CoreModules.Framework.Monitoring m => m.Scene.StatsReporter.LastReportedSimStats[11], m => string.Format("{0} ms", m.GetValue()))); - m_alerts.Add(new DeadlockAlert(m_monitors.Find(x => x is LastFrameTimeMonitor) as LastFrameTimeMonitor)); + m_alerts.Add(new DeadlockAlert(m_staticMonitors.Find(x => x is LastFrameTimeMonitor) as LastFrameTimeMonitor)); foreach (IAlert alert in m_alerts) { diff --git a/OpenSim/Region/Framework/Scenes/SimStatsReporter.cs b/OpenSim/Region/Framework/Scenes/SimStatsReporter.cs index 5c56264ec4..08d8d7c28f 100644 --- a/OpenSim/Region/Framework/Scenes/SimStatsReporter.cs +++ b/OpenSim/Region/Framework/Scenes/SimStatsReporter.cs @@ -26,7 +26,7 @@ */ using System; -//using System.Collections.Generic; +using System.Collections.Generic; using System.Timers; using OpenMetaverse.Packets; using OpenSim.Framework; @@ -35,10 +35,18 @@ using OpenSim.Region.Framework.Interfaces; namespace OpenSim.Region.Framework.Scenes { + /// + /// Collect statistics from the scene to send to the client and for access by other monitoring tools. + /// + /// + /// FIXME: This should be a monitoring region module + /// public class SimStatsReporter { -// private static readonly log4net.ILog m_log -// = log4net.LogManager.GetLogger(System.Reflection.MethodBase.GetCurrentMethod().DeclaringType); + private static readonly log4net.ILog m_log + = log4net.LogManager.GetLogger(System.Reflection.MethodBase.GetCurrentMethod().DeclaringType); + + public const string LastReportedObjectUpdateStatName = "LastReportedObjectUpdates"; public delegate void SendStatResult(SimStats stats); @@ -100,6 +108,14 @@ namespace OpenSim.Region.Framework.Scenes get { return lastReportedSimStats; } } + /// + /// Extra sim statistics that are used by monitors but not sent to the client. + /// + /// + /// The keys are the stat names. + /// + private Dictionary m_lastReportedExtraSimStats = new Dictionary(); + // Sending a stats update every 3 seconds- private int statsUpdatesEveryMS = 3000; private float statsUpdateFactor = 0; @@ -334,7 +350,20 @@ namespace OpenSim.Region.Framework.Scenes } // Extra statistics that aren't currently sent to clients - LastReportedObjectUpdates = m_objectUpdates / statsUpdateFactor; + lock (m_lastReportedExtraSimStats) + { + m_lastReportedExtraSimStats[LastReportedObjectUpdateStatName] = m_objectUpdates / statsUpdateFactor; + + Dictionary physicsStats = m_scene.PhysicsScene.GetStats(); + + if (physicsStats != null) + { + foreach (KeyValuePair tuple in physicsStats) + { + m_lastReportedExtraSimStats[tuple.Key] = tuple.Value / statsUpdateFactor; + } + } + } resetvalues(); } @@ -487,7 +516,10 @@ namespace OpenSim.Region.Framework.Scenes public void AddPendingDownloads(int count) { m_pendingDownloads += count; - if (m_pendingDownloads < 0) m_pendingDownloads = 0; + + if (m_pendingDownloads < 0) + m_pendingDownloads = 0; + //m_log.InfoFormat("[stats]: Adding {0} to pending downloads to make {1}", count, m_pendingDownloads); } @@ -509,5 +541,11 @@ namespace OpenSim.Region.Framework.Scenes } #endregion + + public Dictionary GetExtraSimStats() + { + lock (m_lastReportedExtraSimStats) + return new Dictionary(m_lastReportedExtraSimStats); + } } } diff --git a/OpenSim/Region/Physics/Manager/PhysicsScene.cs b/OpenSim/Region/Physics/Manager/PhysicsScene.cs index 2a6163c0e5..b32cd30bec 100644 --- a/OpenSim/Region/Physics/Manager/PhysicsScene.cs +++ b/OpenSim/Region/Physics/Manager/PhysicsScene.cs @@ -192,8 +192,22 @@ namespace OpenSim.Region.Physics.Manager public abstract void AddPhysicsActorTaint(PhysicsActor prim); + /// + /// Perform a simulation of the current physics scene over the given timestep. + /// + /// + /// The number of frames simulated over that period. public abstract float Simulate(float timeStep); + /// + /// Get statistics about this scene. + /// + /// This facility is currently experimental and subject to change. + /// + /// A dictionary where the key is the statistic name. If no statistics are supplied then returns null. + /// + public virtual Dictionary GetStats() { return null; } + public abstract void GetResults(); public abstract void SetTerrain(float[] heightMap); diff --git a/OpenSim/Region/Physics/OdePlugin/OdeScene.cs b/OpenSim/Region/Physics/OdePlugin/OdeScene.cs index 409b27be02..fa659453c1 100644 --- a/OpenSim/Region/Physics/OdePlugin/OdeScene.cs +++ b/OpenSim/Region/Physics/OdePlugin/OdeScene.cs @@ -131,6 +131,41 @@ namespace OpenSim.Region.Physics.OdePlugin /// internal static Object UniversalColliderSyncObject = new Object(); + /// + /// Is stats collecting enabled for this ODE scene? + /// + public bool CollectStats { get; set; } + + /// + /// Statistics for this scene. + /// + private Dictionary m_stats = new Dictionary(); + + /// + /// Stat name for recording the number of milliseconds that ODE spends in native collision code. + /// + public const string ODENativeCollisionFrameMsStatName = "ODENativeCollisionFrameMS"; + + /// + /// Used to hold tick numbers for stat collection purposes. + /// + private int m_nativeCollisionTickRecorder; + + /// + /// 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; + + /// + /// Used in calculating physics frame time dilation + /// + private int tickCountFrameRun; + + /// + /// Used in calculating physics frame time dilation + /// + private int latertickcount; + private Random fluidRandomizer = new Random(Environment.TickCount); private const uint m_regionWidth = Constants.RegionSize; @@ -345,9 +380,6 @@ namespace OpenSim.Region.Physics.OdePlugin private OdePrim cp1; private OdeCharacter cc2; private OdePrim cp2; - private int tickCountFrameRun; - - private int latertickcount=0; //private int cStartStop = 0; //private string cDictKey = ""; @@ -440,6 +472,8 @@ namespace OpenSim.Region.Physics.OdePlugin // Initialize the mesh plugin public override void Initialise(IMesher meshmerizer, IConfigSource config) { + m_stats[ODENativeCollisionFrameMsStatName] = 0; + mesher = meshmerizer; m_config = config; // Defaults @@ -464,6 +498,8 @@ namespace OpenSim.Region.Physics.OdePlugin IConfig physicsconfig = m_config.Configs["ODEPhysicsSettings"]; if (physicsconfig != null) { + CollectStats = physicsconfig.GetBoolean("collect_stats", false); + gravityx = physicsconfig.GetFloat("world_gravityx", 0f); gravityy = physicsconfig.GetFloat("world_gravityy", 0f); gravityz = physicsconfig.GetFloat("world_gravityz", -9.8f); @@ -764,6 +800,62 @@ namespace OpenSim.Region.Physics.OdePlugin #region Collision Detection + /// + /// Collides two geometries. + /// + /// + /// + /// /param> + /// + /// + /// + private int CollideGeoms( + IntPtr geom1, IntPtr geom2, int maxContacts, Ode.NET.d.ContactGeom[] contactsArray, int contactGeomSize) + { + int count; + + lock (OdeScene.UniversalColliderSyncObject) + { + // We do this inside the lock so that we don't count any delay in acquiring it + if (CollectStats) + m_nativeCollisionTickRecorder = Util.EnvironmentTickCount(); + + count = d.Collide(geom1, geom2, maxContacts, contactsArray, contactGeomSize); + } + + // 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); + + return count; + } + + /// + /// Collide two spaces or a space and a geometry. + /// + /// + /// /param> + /// + private void CollideSpaces(IntPtr space1, IntPtr space2, IntPtr data) + { + if (CollectStats) + { + m_inCollisionTiming = true; + m_nativeCollisionTickRecorder = Util.EnvironmentTickCount(); + } + + d.SpaceCollide2(space1, space2, data, nearCallback); + + if (CollectStats && m_inCollisionTiming) + { + m_stats[ODENativeCollisionFrameMsStatName] + += Util.EnvironmentTickCountSubtract(m_nativeCollisionTickRecorder); + m_inCollisionTiming = false; + } + } + /// /// This is our near callback. A geometry is near a body /// @@ -772,6 +864,13 @@ namespace OpenSim.Region.Physics.OdePlugin /// another geometry or space private void near(IntPtr space, IntPtr g1, IntPtr g2) { + if (CollectStats && m_inCollisionTiming) + { + m_stats[ODENativeCollisionFrameMsStatName] + += Util.EnvironmentTickCountSubtract(m_nativeCollisionTickRecorder); + m_inCollisionTiming = false; + } + // m_log.DebugFormat("[PHYSICS]: Colliding {0} and {1} in {2}", g1, g2, space); // no lock here! It's invoked from within Simulate(), which is thread-locked @@ -789,7 +888,7 @@ namespace OpenSim.Region.Physics.OdePlugin // contact points in the space try { - d.SpaceCollide2(g1, g2, IntPtr.Zero, nearCallback); + CollideSpaces(g1, g2, IntPtr.Zero); } catch (AccessViolationException) { @@ -832,6 +931,7 @@ namespace OpenSim.Region.Physics.OdePlugin // Figure out how many contact points we have int count = 0; + try { // Colliding Geom To Geom @@ -843,8 +943,7 @@ namespace OpenSim.Region.Physics.OdePlugin if (b1 != IntPtr.Zero && b2 != IntPtr.Zero && d.AreConnectedExcluding(b1, b2, d.JointType.Contact)) return; - lock (OdeScene.UniversalColliderSyncObject) - count = d.Collide(g1, g2, contacts.Length, contacts, d.ContactGeom.SizeOf); + count = CollideGeoms(g1, g2, contacts.Length, contacts, d.ContactGeom.SizeOf); if (count > contacts.Length) m_log.Error("[ODE SCENE]: Got " + count + " contacts when we asked for a maximum of " + contacts.Length); @@ -1578,7 +1677,7 @@ namespace OpenSim.Region.Physics.OdePlugin // and we'll run it again on all of them. try { - d.SpaceCollide2(space, chr.Shell, IntPtr.Zero, nearCallback); + CollideSpaces(space, chr.Shell, IntPtr.Zero); } catch (AccessViolationException) { @@ -1593,6 +1692,9 @@ namespace OpenSim.Region.Physics.OdePlugin //} } +// if (framecount % 55 == 0) +// m_log.DebugFormat("Processed {0} collisions", _perloopContact.Count); + List removeprims = null; foreach (OdePrim chr in _activeprims) { @@ -1604,7 +1706,7 @@ namespace OpenSim.Region.Physics.OdePlugin { if (space != IntPtr.Zero && chr.prim_geom != IntPtr.Zero && chr.m_taintremove == false) { - d.SpaceCollide2(space, chr.prim_geom, IntPtr.Zero, nearCallback); + CollideSpaces(space, chr.prim_geom, IntPtr.Zero); } else { @@ -2689,7 +2791,7 @@ namespace OpenSim.Region.Physics.OdePlugin /// 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) { if (framecount >= int.MaxValue) @@ -3190,7 +3292,7 @@ namespace OpenSim.Region.Physics.OdePlugin public override bool IsThreaded { // for now we won't be multithreaded - get { return (false); } + get { return false; } } #region ODE Specific Terrain Fixes @@ -3955,5 +4057,22 @@ namespace OpenSim.Region.Physics.OdePlugin ds.SetViewpoint(ref xyz, ref hpr); } #endif + + public override Dictionary GetStats() + { + if (!CollectStats) + return null; + + Dictionary returnStats; + + lock (OdeLock) + { + returnStats = new Dictionary(m_stats); + + m_stats[ODENativeCollisionFrameMsStatName] = 0; + } + + return returnStats; + } } } \ No newline at end of file diff --git a/bin/OpenSimDefaults.ini b/bin/OpenSimDefaults.ini index 5da3ba2c9b..27d86e8f8c 100644 --- a/bin/OpenSimDefaults.ini +++ b/bin/OpenSimDefaults.ini @@ -675,6 +675,25 @@ [ODEPhysicsSettings] + ; ## + ; ## Physics stats settings + ; + + ; If collect_stats is enabled, then extra stat information is collected which is accessible via the MonitorModule + ; (see http://opensimulator.org/wiki/Monitoring_Module for more details). + collect_stats = false + + ; ## + ; ## Physics logging settings - logfiles are saved to *.DIF files + ; ## + + ; default is false + ;physics_logging = true + ;; every n simulation iterations, the physics snapshot file is updated + ;physics_logging_interval = 50 + ;; append to existing physics logfile, or overwrite existing logfiles? + ;physics_logging_append_existing_logfile = true + ;## ;## World Settings ;## @@ -823,17 +842,6 @@ ; number^2 physical level of detail of the sculpt texture. 16x16 - 256 verticies mesh_physical_lod = 16 - ; ## - ; ## Physics logging settings - logfiles are saved to *.DIF files - ; ## - - ; default is false - ;physics_logging = true - ;; every n simulation iterations, the physics snapshot file is updated - ;physics_logging_interval = 50 - ;; append to existing physics logfile, or overwrite existing logfiles? - ;physics_logging_append_existing_logfile = true - ; ## ; ## Joint support ; ## From 878b67b333320070f643dfdd11e0a9c6ff453543 Mon Sep 17 00:00:00 2001 From: "Justin Clark-Casey (justincc)" Date: Fri, 1 Jun 2012 00:26:11 +0100 Subject: [PATCH 03/10] Fix OdeScene.GetTopColliders() to return the top 25 colliders rather than the first 25 that had non-zero collision scores. Also zeros collisions scores on all prims after report collection, not just the top 25. As before, this collision scores are only reset after a report is requested, which may give unrealistic numbers on the first request. So to see more realistic scores, ignore the first report and then refresh the request after a couple of seconds or so. --- OpenSim/Region/Physics/OdePlugin/OdeScene.cs | 32 ++++++++------------ 1 file changed, 13 insertions(+), 19 deletions(-) diff --git a/OpenSim/Region/Physics/OdePlugin/OdeScene.cs b/OpenSim/Region/Physics/OdePlugin/OdeScene.cs index fa659453c1..25b3266f2b 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 { @@ -3868,26 +3869,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() From 93fa9e89918f41db01229c61a228724d380552ac Mon Sep 17 00:00:00 2001 From: "Justin Clark-Casey (justincc)" Date: Fri, 1 Jun 2012 00:56:13 +0100 Subject: [PATCH 04/10] Add ODE avatar and prim collision numbers if extra stats collection is enabled. --- OpenSim/Region/Physics/OdePlugin/OdeScene.cs | 39 +++++++++++++++++--- 1 file changed, 34 insertions(+), 5 deletions(-) diff --git a/OpenSim/Region/Physics/OdePlugin/OdeScene.cs b/OpenSim/Region/Physics/OdePlugin/OdeScene.cs index 25b3266f2b..864cdc23d1 100644 --- a/OpenSim/Region/Physics/OdePlugin/OdeScene.cs +++ b/OpenSim/Region/Physics/OdePlugin/OdeScene.cs @@ -147,6 +147,16 @@ namespace OpenSim.Region.Physics.OdePlugin /// public const string ODENativeCollisionFrameMsStatName = "ODENativeCollisionFrameMS"; + /// + /// Stat name for the number of avatar collisions with another entity. + /// + public const string ODEAvatarCollisionsStatName = "ODEAvatarCollisions"; + + /// + /// Stat name for the number of prim collisions with another entity. + /// + public const string ODEPrimCollisionsStatName = "ODEPrimCollisions"; + /// /// Used to hold tick numbers for stat collection purposes. /// @@ -157,6 +167,12 @@ namespace OpenSim.Region.Physics.OdePlugin /// 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 /// @@ -473,7 +489,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; @@ -1455,7 +1471,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)); } } @@ -1693,8 +1709,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[ODEAvatarCollisionsStatName] += m_tempAvatarCollisionsThisFrame; + } List removeprims = null; foreach (OdePrim chr in _activeprims) @@ -1728,6 +1747,9 @@ namespace OpenSim.Region.Physics.OdePlugin } } + if (CollectStats) + m_stats[ODEPrimCollisionsStatName] += _perloopContact.Count - m_tempAvatarCollisionsThisFrame; + if (removeprims != null) { foreach (OdePrim chr in removeprims) @@ -4063,10 +4085,17 @@ namespace OpenSim.Region.Physics.OdePlugin { returnStats = new Dictionary(m_stats); - m_stats[ODENativeCollisionFrameMsStatName] = 0; + InitializeExtraStats(); } return returnStats; } + + private void InitializeExtraStats() + { + m_stats[ODENativeCollisionFrameMsStatName] = 0; + m_stats[ODEAvatarCollisionsStatName] = 0; + m_stats[ODEPrimCollisionsStatName] = 0; + } } } \ No newline at end of file From 8301f7b17f8e524d2412f927530da95f711bd6ac Mon Sep 17 00:00:00 2001 From: "Justin Clark-Casey (justincc)" Date: Fri, 1 Jun 2012 00:57:55 +0100 Subject: [PATCH 05/10] minor: comment out currently unused OdeScene.sCollisionData --- OpenSim/Region/Physics/OdePlugin/OdeScene.cs | 18 +++++++++--------- 1 file changed, 9 insertions(+), 9 deletions(-) diff --git a/OpenSim/Region/Physics/OdePlugin/OdeScene.cs b/OpenSim/Region/Physics/OdePlugin/OdeScene.cs index 864cdc23d1..ace0ba515f 100644 --- a/OpenSim/Region/Physics/OdePlugin/OdeScene.cs +++ b/OpenSim/Region/Physics/OdePlugin/OdeScene.cs @@ -55,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 From e1f8d2adb0dc1dffad8caf47611217b1f7f14f47 Mon Sep 17 00:00:00 2001 From: "Justin Clark-Casey (justincc)" Date: Fri, 1 Jun 2012 01:12:30 +0100 Subject: [PATCH 06/10] Stop adding an unnecessary duplicate _perloopcontact if the avatar is standing on a prim. This has already been added earlier on in the method. --- OpenSim/Region/Physics/OdePlugin/OdeScene.cs | 13 ++++--------- 1 file changed, 4 insertions(+), 9 deletions(-) diff --git a/OpenSim/Region/Physics/OdePlugin/OdeScene.cs b/OpenSim/Region/Physics/OdePlugin/OdeScene.cs index ace0ba515f..55c7e5a571 100644 --- a/OpenSim/Region/Physics/OdePlugin/OdeScene.cs +++ b/OpenSim/Region/Physics/OdePlugin/OdeScene.cs @@ -1229,14 +1229,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) @@ -1249,7 +1247,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) @@ -1344,13 +1342,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) @@ -1361,9 +1357,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) { From c33c8db8256225b5ec09c0767c8b65341964d678 Mon Sep 17 00:00:00 2001 From: "Justin Clark-Casey (justincc)" Date: Fri, 1 Jun 2012 01:15:27 +0100 Subject: [PATCH 07/10] Rename new collision stats to 'contacts' - there are/can be multiple contacts per collision and this is what is actually being measured. --- OpenSim/Region/Physics/OdePlugin/OdeScene.cs | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/OpenSim/Region/Physics/OdePlugin/OdeScene.cs b/OpenSim/Region/Physics/OdePlugin/OdeScene.cs index 55c7e5a571..32dac22bfb 100644 --- a/OpenSim/Region/Physics/OdePlugin/OdeScene.cs +++ b/OpenSim/Region/Physics/OdePlugin/OdeScene.cs @@ -150,12 +150,12 @@ namespace OpenSim.Region.Physics.OdePlugin /// /// Stat name for the number of avatar collisions with another entity. /// - public const string ODEAvatarCollisionsStatName = "ODEAvatarCollisions"; + public const string ODEAvatarContactsStatsName = "ODEAvatarContacts"; /// /// Stat name for the number of prim collisions with another entity. /// - public const string ODEPrimCollisionsStatName = "ODEPrimCollisions"; + public const string ODEPrimContactsStatName = "ODEPrimContacts"; /// /// Used to hold tick numbers for stat collection purposes. @@ -1707,7 +1707,7 @@ namespace OpenSim.Region.Physics.OdePlugin if (CollectStats) { m_tempAvatarCollisionsThisFrame = _perloopContact.Count; - m_stats[ODEAvatarCollisionsStatName] += m_tempAvatarCollisionsThisFrame; + m_stats[ODEAvatarContactsStatsName] += m_tempAvatarCollisionsThisFrame; } List removeprims = null; @@ -1743,7 +1743,7 @@ namespace OpenSim.Region.Physics.OdePlugin } if (CollectStats) - m_stats[ODEPrimCollisionsStatName] += _perloopContact.Count - m_tempAvatarCollisionsThisFrame; + m_stats[ODEPrimContactsStatName] += _perloopContact.Count - m_tempAvatarCollisionsThisFrame; if (removeprims != null) { @@ -4089,8 +4089,8 @@ namespace OpenSim.Region.Physics.OdePlugin private void InitializeExtraStats() { m_stats[ODENativeCollisionFrameMsStatName] = 0; - m_stats[ODEAvatarCollisionsStatName] = 0; - m_stats[ODEPrimCollisionsStatName] = 0; + m_stats[ODEAvatarContactsStatsName] = 0; + m_stats[ODEPrimContactsStatName] = 0; } } } \ No newline at end of file From 8333b928fa3353304358ed55293b52478a39ab6e Mon Sep 17 00:00:00 2001 From: "Justin Clark-Casey (justincc)" Date: Fri, 1 Jun 2012 01:27:19 +0100 Subject: [PATCH 08/10] Break down native ODE collision frame time stat into native space collision and geom collision stats --- OpenSim/Region/Physics/OdePlugin/OdeScene.cs | 26 +++++++++++++++++--- 1 file changed, 22 insertions(+), 4 deletions(-) diff --git a/OpenSim/Region/Physics/OdePlugin/OdeScene.cs b/OpenSim/Region/Physics/OdePlugin/OdeScene.cs index 32dac22bfb..d4c0b85e4a 100644 --- a/OpenSim/Region/Physics/OdePlugin/OdeScene.cs +++ b/OpenSim/Region/Physics/OdePlugin/OdeScene.cs @@ -147,6 +147,16 @@ namespace OpenSim.Region.Physics.OdePlugin /// public const string ODENativeCollisionFrameMsStatName = "ODENativeCollisionFrameMS"; + /// + /// Stat name for recording the number of milliseconds that ODE spends in native space collision code. + /// + public const string ODENativeSpaceCollisionFrameMsStatName = "ODENativeSpaceCollisionFrameMS"; + + /// + /// Stat name for recording the number of milliseconds that ODE spends in native geom collision code. + /// + public const string ODENativeGeomCollisionFrameMsStatName = "ODENativeGeomCollisionFrameMS"; + /// /// Stat name for the number of avatar collisions with another entity. /// @@ -843,7 +853,7 @@ 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] + m_stats[ODENativeGeomCollisionFrameMsStatName] += Util.EnvironmentTickCountSubtract(m_nativeCollisionTickRecorder); return count; @@ -867,7 +877,7 @@ namespace OpenSim.Region.Physics.OdePlugin if (CollectStats && m_inCollisionTiming) { - m_stats[ODENativeCollisionFrameMsStatName] + m_stats[ODENativeSpaceCollisionFrameMsStatName] += Util.EnvironmentTickCountSubtract(m_nativeCollisionTickRecorder); m_inCollisionTiming = false; } @@ -883,7 +893,7 @@ namespace OpenSim.Region.Physics.OdePlugin { if (CollectStats && m_inCollisionTiming) { - m_stats[ODENativeCollisionFrameMsStatName] + m_stats[ODENativeSpaceCollisionFrameMsStatName] += Util.EnvironmentTickCountSubtract(m_nativeCollisionTickRecorder); m_inCollisionTiming = false; } @@ -4079,6 +4089,10 @@ namespace OpenSim.Region.Physics.OdePlugin lock (OdeLock) { returnStats = new Dictionary(m_stats); + + returnStats[ODENativeCollisionFrameMsStatName] + = returnStats[ODENativeSpaceCollisionFrameMsStatName] + + returnStats[ODENativeGeomCollisionFrameMsStatName]; InitializeExtraStats(); } @@ -4088,7 +4102,11 @@ namespace OpenSim.Region.Physics.OdePlugin private void InitializeExtraStats() { - m_stats[ODENativeCollisionFrameMsStatName] = 0; + // No need to zero since this is calculated by addition + // m_stats[ODENativeCollisionFrameMsStatName] = 0; + + m_stats[ODENativeSpaceCollisionFrameMsStatName] = 0; + m_stats[ODENativeGeomCollisionFrameMsStatName] = 0; m_stats[ODEAvatarContactsStatsName] = 0; m_stats[ODEPrimContactsStatName] = 0; } From f2c8c7a7b8cd6a3d3cbbaafa2ba266658b7d0998 Mon Sep 17 00:00:00 2001 From: "Justin Clark-Casey (justincc)" Date: Fri, 1 Jun 2012 01:37:19 +0100 Subject: [PATCH 09/10] Add total ODE frame time optional stat, as a sanity check on the main scene physics stat --- OpenSim/Region/Physics/OdePlugin/OdeScene.cs | 28 +++++++++++++++----- 1 file changed, 21 insertions(+), 7 deletions(-) diff --git a/OpenSim/Region/Physics/OdePlugin/OdeScene.cs b/OpenSim/Region/Physics/OdePlugin/OdeScene.cs index d4c0b85e4a..ab03696472 100644 --- a/OpenSim/Region/Physics/OdePlugin/OdeScene.cs +++ b/OpenSim/Region/Physics/OdePlugin/OdeScene.cs @@ -142,6 +142,14 @@ namespace OpenSim.Region.Physics.OdePlugin /// private Dictionary m_stats = new Dictionary(); + /// + /// 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 recording the number of milliseconds that ODE spends in native collision code. /// @@ -170,7 +178,7 @@ namespace OpenSim.Region.Physics.OdePlugin /// /// 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. @@ -845,7 +853,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); } @@ -854,7 +862,7 @@ namespace OpenSim.Region.Physics.OdePlugin // negligable if (CollectStats) m_stats[ODENativeGeomCollisionFrameMsStatName] - += Util.EnvironmentTickCountSubtract(m_nativeCollisionTickRecorder); + += Util.EnvironmentTickCountSubtract(m_nativeCollisionStartTick); return count; } @@ -870,7 +878,7 @@ namespace OpenSim.Region.Physics.OdePlugin if (CollectStats) { m_inCollisionTiming = true; - m_nativeCollisionTickRecorder = Util.EnvironmentTickCount(); + m_nativeCollisionStartTick = Util.EnvironmentTickCount(); } d.SpaceCollide2(space1, space2, data, nearCallback); @@ -878,7 +886,7 @@ namespace OpenSim.Region.Physics.OdePlugin if (CollectStats && m_inCollisionTiming) { m_stats[ODENativeSpaceCollisionFrameMsStatName] - += Util.EnvironmentTickCountSubtract(m_nativeCollisionTickRecorder); + += Util.EnvironmentTickCountSubtract(m_nativeCollisionStartTick); m_inCollisionTiming = false; } } @@ -894,7 +902,7 @@ namespace OpenSim.Region.Physics.OdePlugin if (CollectStats && m_inCollisionTiming) { m_stats[ODENativeSpaceCollisionFrameMsStatName] - += Util.EnvironmentTickCountSubtract(m_nativeCollisionTickRecorder); + += Util.EnvironmentTickCountSubtract(m_nativeCollisionStartTick); m_inCollisionTiming = false; } @@ -2822,6 +2830,8 @@ namespace OpenSim.Region.Physics.OdePlugin /// The number of frames simulated over that period. public override float Simulate(float timeStep) { + int startFrameTick = Util.EnvironmentTickCount(); + if (framecount >= int.MaxValue) framecount = 0; @@ -3087,6 +3097,9 @@ namespace OpenSim.Region.Physics.OdePlugin tickCountFrameRun = Util.EnvironmentTickCount(); } + if (CollectStats) + m_stats[ODETotalFrameMsStatName] += Util.EnvironmentTickCount() - startFrameTick; + return fps; } @@ -4089,7 +4102,7 @@ namespace OpenSim.Region.Physics.OdePlugin lock (OdeLock) { returnStats = new Dictionary(m_stats); - + returnStats[ODENativeCollisionFrameMsStatName] = returnStats[ODENativeSpaceCollisionFrameMsStatName] + returnStats[ODENativeGeomCollisionFrameMsStatName]; @@ -4105,6 +4118,7 @@ namespace OpenSim.Region.Physics.OdePlugin // No need to zero since this is calculated by addition // m_stats[ODENativeCollisionFrameMsStatName] = 0; + m_stats[ODETotalFrameMsStatName] = 0; m_stats[ODENativeSpaceCollisionFrameMsStatName] = 0; m_stats[ODENativeGeomCollisionFrameMsStatName] = 0; m_stats[ODEAvatarContactsStatsName] = 0; From 5cc9b820e5b0a9490e6499ee5151c5e698c3e110 Mon Sep 17 00:00:00 2001 From: "Justin Clark-Casey (justincc)" Date: Fri, 1 Jun 2012 01:58:28 +0100 Subject: [PATCH 10/10] Add option native step frame ms stat --- OpenSim/Region/Physics/OdePlugin/OdeScene.cs | 30 ++++++++++++++------ 1 file changed, 21 insertions(+), 9 deletions(-) diff --git a/OpenSim/Region/Physics/OdePlugin/OdeScene.cs b/OpenSim/Region/Physics/OdePlugin/OdeScene.cs index ab03696472..04c4722b37 100644 --- a/OpenSim/Region/Physics/OdePlugin/OdeScene.cs +++ b/OpenSim/Region/Physics/OdePlugin/OdeScene.cs @@ -150,6 +150,11 @@ namespace OpenSim.Region.Physics.OdePlugin /// public const string ODETotalFrameMsStatName = "ODETotalFrameMS"; + /// + /// The amount of time spent in native code that actually steps through the simulation. + /// + public const string ODENativeStepFrameMsStatName = "ODENativeStepFrameMS"; + /// /// Stat name for recording the number of milliseconds that ODE spends in native collision code. /// @@ -2821,23 +2826,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 = Util.EnvironmentTickCount(); + int startFrameTick = CollectStats ? Util.EnvironmentTickCount() : 0; + int quickStepTick = 0; if (framecount >= int.MaxValue) framecount = 0; - //if (m_worldOffset != Vector3.Zero) - // return 0; - framecount++; float fps = 0; @@ -2845,7 +2850,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 @@ -3007,9 +3012,15 @@ namespace OpenSim.Region.Physics.OdePlugin // "[PHYSICS]: Collision contacts to process this frame = {0}", m_global_contactcount); m_global_contactcount = 0; - + + if (CollectStats) + quickStepTick = Util.EnvironmentTickCount(); + d.WorldQuickStep(world, ODE_STEPSIZE); + if (CollectStats) + m_stats[ODENativeStepFrameMsStatName] += Util.EnvironmentTickCountSubtract(quickStepTick); + d.JointGroupEmpty(contactgroup); } catch (Exception e) @@ -3077,7 +3088,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. @@ -3098,7 +3109,7 @@ namespace OpenSim.Region.Physics.OdePlugin } if (CollectStats) - m_stats[ODETotalFrameMsStatName] += Util.EnvironmentTickCount() - startFrameTick; + m_stats[ODETotalFrameMsStatName] += Util.EnvironmentTickCountSubtract(startFrameTick); return fps; } @@ -4119,6 +4130,7 @@ namespace OpenSim.Region.Physics.OdePlugin // m_stats[ODENativeCollisionFrameMsStatName] = 0; m_stats[ODETotalFrameMsStatName] = 0; + m_stats[ODENativeStepFrameMsStatName] = 0; m_stats[ODENativeSpaceCollisionFrameMsStatName] = 0; m_stats[ODENativeGeomCollisionFrameMsStatName] = 0; m_stats[ODEAvatarContactsStatsName] = 0;