From e20cf3789bc8bbcda86c8e9067fbe8ecdb3046ac Mon Sep 17 00:00:00 2001 From: Justin Clark-Casey (justincc) Date: Wed, 25 Jan 2012 19:31:50 +0000 Subject: Serialize calls to ODE Collide() function across OdeScene instances to prevent ODE crashes on simulators running more than one region. It turns out that calls to Collide() are not thread-safe even for objects in different ODE physics worlds due to ODE static caches. For simulators running multiple regions, not serializing calls from different scene loops will sooner or later cause OpenSim to crash with a native stack trace referencing OBBCollider. This affects the default OPCODE collider but not GIMPACT. However, GIMPACT fails for other reasons under some current simulator loads. ODE provides a thread local storage option, but as of ODE r1755 (and r1840) DLLs compiled with this crash OpenSim immediately. --- OpenSim/Region/Physics/OdePlugin/ODECharacter.cs | 3 +++ OpenSim/Region/Physics/OdePlugin/OdeScene.cs | 30 +++++++++++++++++++++++- 2 files changed, 32 insertions(+), 1 deletion(-) (limited to 'OpenSim/Region/Physics') diff --git a/OpenSim/Region/Physics/OdePlugin/ODECharacter.cs b/OpenSim/Region/Physics/OdePlugin/ODECharacter.cs index c6e8286..7c1c046 100644 --- a/OpenSim/Region/Physics/OdePlugin/ODECharacter.cs +++ b/OpenSim/Region/Physics/OdePlugin/ODECharacter.cs @@ -1048,6 +1048,7 @@ namespace OpenSim.Region.Physics.OdePlugin CAPSULE_RADIUS = 0.01f; } +// lock (OdeScene.UniversalColliderSyncObject) Shell = d.CreateCapsule(_parent_scene.space, CAPSULE_RADIUS, CAPSULE_LENGTH); d.GeomSetCategoryBits(Shell, (int)m_collisionCategories); @@ -1179,7 +1180,9 @@ namespace OpenSim.Region.Physics.OdePlugin if (Shell != IntPtr.Zero) { +// lock (OdeScene.UniversalColliderSyncObject) d.GeomDestroy(Shell); + _parent_scene.geom_name_map.Remove(Shell); _parent_scene.actor_name_map.Remove(Shell); diff --git a/OpenSim/Region/Physics/OdePlugin/OdeScene.cs b/OpenSim/Region/Physics/OdePlugin/OdeScene.cs index 37daf46..4530c09 100644 --- a/OpenSim/Region/Physics/OdePlugin/OdeScene.cs +++ b/OpenSim/Region/Physics/OdePlugin/OdeScene.cs @@ -105,6 +105,32 @@ namespace OpenSim.Region.Physics.OdePlugin private readonly ILog m_log; // private Dictionary m_storedCollisions = new Dictionary(); + /// + /// Provide a sync object so that only one thread calls d.Collide() at a time across all OdeScene instances. + /// + /// + /// With ODE as of r1755 (though also tested on r1860), only one thread can call d.Collide() at a + /// time, even where physics objects are in entirely different ODE worlds. This is because generating contacts + /// uses a static cache at the ODE level. + /// + /// Without locking, simulators running multiple regions will eventually crash with a native stack trace similar + /// to + /// + /// mono() [0x489171] + /// mono() [0x4d154f] + /// /lib/x86_64-linux-gnu/libpthread.so.0(+0xfc60) [0x7f6ded592c60] + /// .../opensim/bin/libode-x86_64.so(_ZN6Opcode11OBBCollider8_CollideEPKNS_14AABBNoLeafNodeE+0xd7a) [0x7f6dd822628a] + /// + /// ODE provides an experimental option to cache in thread local storage but compiling ODE with this option + /// causes OpenSimulator to immediately crash with a native stack trace similar to + /// + /// mono() [0x489171] + /// mono() [0x4d154f] + /// /lib/x86_64-linux-gnu/libpthread.so.0(+0xfc60) [0x7f03c9849c60] + /// .../opensim/bin/libode-x86_64.so(_Z12dCollideCCTLP6dxGeomS0_iP12dContactGeomi+0x92) [0x7f03b44bcf82] + /// + internal static Object UniversalColliderSyncObject = new Object(); + private Random fluidRandomizer = new Random(Environment.TickCount); private const uint m_regionWidth = Constants.RegionSize; @@ -799,7 +825,9 @@ namespace OpenSim.Region.Physics.OdePlugin if (b1 != IntPtr.Zero && b2 != IntPtr.Zero && d.AreConnectedExcluding(b1, b2, d.JointType.Contact)) return; - count = d.Collide(g1, g2, contacts.Length, contacts, d.ContactGeom.SizeOf); + lock (OdeScene.UniversalColliderSyncObject) + count = d.Collide(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); } -- cgit v1.1 From e9de7e7107f4a52a944a06e20db0f29d0d6d37d3 Mon Sep 17 00:00:00 2001 From: Robert Adams Date: Wed, 25 Jan 2012 14:40:38 -0800 Subject: Update BulletSim.dll with some interface changes and tuning (see opensim-libs). Change BSScene to use new interface. --- OpenSim/Region/Physics/BulletSPlugin/BSScene.cs | 33 ++++++++++++++++++---- .../Region/Physics/BulletSPlugin/BulletSimAPI.cs | 4 +++ 2 files changed, 31 insertions(+), 6 deletions(-) (limited to 'OpenSim/Region/Physics') diff --git a/OpenSim/Region/Physics/BulletSPlugin/BSScene.cs b/OpenSim/Region/Physics/BulletSPlugin/BSScene.cs index 7704002..e9a849c 100644 --- a/OpenSim/Region/Physics/BulletSPlugin/BSScene.cs +++ b/OpenSim/Region/Physics/BulletSPlugin/BSScene.cs @@ -72,6 +72,8 @@ public class BSScene : PhysicsScene, IPhysicsParameters private bool m_initialized = false; + private int m_detailedStatsStep = 0; + public IMesher mesher; private float m_meshLOD; public float MeshLOD @@ -192,6 +194,8 @@ public class BSScene : PhysicsScene, IPhysicsParameters m_meshLOD = 8f; m_sculptLOD = 32f; + m_detailedStatsStep = 0; // disabled + m_maxSubSteps = 10; m_fixedTimeStep = 1f / 60f; m_maxCollisionsPerFrame = 2048; @@ -209,8 +213,9 @@ public class BSScene : PhysicsScene, IPhysicsParameters parms.deactivationTime = 0.2f; parms.linearSleepingThreshold = 0.8f; parms.angularSleepingThreshold = 1.0f; - parms.ccdMotionThreshold = 0.5f; // set to zero to disable - parms.ccdSweptSphereRadius = 0.2f; + parms.ccdMotionThreshold = 0.0f; // set to zero to disable + parms.ccdSweptSphereRadius = 0.0f; + parms.contactProcessingThreshold = 0.1f; parms.terrainFriction = 0.5f; parms.terrainHitFraction = 0.8f; @@ -231,6 +236,7 @@ public class BSScene : PhysicsScene, IPhysicsParameters _meshSculptedPrim = pConfig.GetBoolean("MeshSculptedPrim", _meshSculptedPrim); _forceSimplePrimMeshing = pConfig.GetBoolean("ForceSimplePrimMeshing", _forceSimplePrimMeshing); + m_detailedStatsStep = pConfig.GetInt("DetailedStatsStep", m_detailedStatsStep); m_meshLOD = pConfig.GetFloat("MeshLevelOfDetail", m_meshLOD); m_sculptLOD = pConfig.GetFloat("SculptLevelOfDetail", m_sculptLOD); @@ -253,6 +259,7 @@ public class BSScene : PhysicsScene, IPhysicsParameters parms.angularSleepingThreshold = pConfig.GetFloat("AngularSleepingThreshold", parms.angularSleepingThreshold); parms.ccdMotionThreshold = pConfig.GetFloat("CcdMotionThreshold", parms.ccdMotionThreshold); parms.ccdSweptSphereRadius = pConfig.GetFloat("CcdSweptSphereRadius", parms.ccdSweptSphereRadius); + parms.contactProcessingThreshold = pConfig.GetFloat("ContactProcessingThreshold", parms.contactProcessingThreshold); parms.terrainFriction = pConfig.GetFloat("TerrainFriction", parms.terrainFriction); parms.terrainHitFraction = pConfig.GetFloat("TerrainHitFraction", parms.terrainHitFraction); @@ -398,6 +405,14 @@ public class BSScene : PhysicsScene, IPhysicsParameters } } + if (m_detailedStatsStep > 0) + { + if ((m_simulationStep % m_detailedStatsStep) == 0) + { + BulletSimAPI.DumpBulletStatistics(); + } + } + // TODO: FIX THIS: fps calculation wrong. This calculation always returns about 1 in normal operation. return timeStep / (numSubSteps * m_fixedTimeStep) * 1000f; } @@ -671,6 +686,7 @@ public class BSScene : PhysicsScene, IPhysicsParameters new PhysParameterEntry("MaxSubStep", "In simulation step, maximum number of substeps"), new PhysParameterEntry("FixedTimeStep", "In simulation step, seconds of one substep (1/60)"), new PhysParameterEntry("MaxObjectMass", "Maximum object mass (10000.01)"), + new PhysParameterEntry("DetailedStats", "Frames between outputting detailed phys stats. Zero is off"), new PhysParameterEntry("DefaultFriction", "Friction factor used on new objects"), new PhysParameterEntry("DefaultDensity", "Density for new objects" ), @@ -685,6 +701,7 @@ public class BSScene : PhysicsScene, IPhysicsParameters new PhysParameterEntry("AngularSleepingThreshold", "Seconds to measure angular movement before considering static" ), // new PhysParameterEntry("CcdMotionThreshold", "" ), // new PhysParameterEntry("CcdSweptSphereRadius", "" ), + new PhysParameterEntry("ContactProcessingThreshold", "Distance between contacts before doing collision check" ), new PhysParameterEntry("TerrainFriction", "Factor to reduce movement against terrain surface" ), new PhysParameterEntry("TerrainHitFraction", "Distance to measure hit collisions" ), @@ -715,6 +732,7 @@ public class BSScene : PhysicsScene, IPhysicsParameters string lparm = parm.ToLower(); switch (lparm) { + case "detailedstats": m_detailedStatsStep = (int)val; break; case "meshlod": m_meshLOD = (int)val; break; case "sculptlod": m_sculptLOD = (int)val; break; case "maxsubstep": m_maxSubSteps = (int)val; break; @@ -725,7 +743,7 @@ public class BSScene : PhysicsScene, IPhysicsParameters case "defaultdensity": m_params[0].defaultDensity = val; break; case "defaultrestitution": m_params[0].defaultRestitution = val; break; case "collisionmargin": m_params[0].collisionMargin = val; break; - case "gravity": m_params[0].gravity = val; TaintedUpdateParameter(lparm, PhysParameterEntry.APPLY_TO_NONE, val); break; + case "gravity": m_params[0].gravity = val; TaintedUpdateParameter(lparm, PhysParameterEntry.APPLY_TO_NONE, val); break; case "lineardamping": UpdateParameterPrims(ref m_params[0].linearDamping, lparm, localID, val); break; case "angulardamping": UpdateParameterPrims(ref m_params[0].angularDamping, lparm, localID, val); break; @@ -734,6 +752,7 @@ public class BSScene : PhysicsScene, IPhysicsParameters case "angularsleepingthreshold": UpdateParameterPrims(ref m_params[0].angularDamping, lparm, localID, val); break; case "ccdmotionthreshold": UpdateParameterPrims(ref m_params[0].ccdMotionThreshold, lparm, localID, val); break; case "ccdsweptsphereradius": UpdateParameterPrims(ref m_params[0].ccdSweptSphereRadius, lparm, localID, val); break; + case "contactprocessingthreshold": UpdateParameterPrims(ref m_params[0].contactProcessingThreshold, lparm, localID, val); break; // set a terrain physical feature and cause terrain to be recalculated case "terrainfriction": m_params[0].terrainFriction = val; TaintedUpdateParameter("terrain", 0, val); break; @@ -741,10 +760,10 @@ public class BSScene : PhysicsScene, IPhysicsParameters case "terrainrestitution": m_params[0].terrainRestitution = val; TaintedUpdateParameter("terrain", 0, val); break; // set an avatar physical feature and cause avatar(s) to be recalculated case "avatarfriction": UpdateParameterAvatars(ref m_params[0].avatarFriction, "avatar", localID, val); break; - case "avatardensity": UpdateParameterAvatars(ref m_params[0].avatarDensity, "avatar", localID, val); break; + case "avatardensity": UpdateParameterAvatars(ref m_params[0].avatarDensity, "avatar", localID, val); break; case "avatarrestitution": UpdateParameterAvatars(ref m_params[0].avatarRestitution, "avatar", localID, val); break; - case "avatarcapsuleradius": UpdateParameterAvatars(ref m_params[0].avatarCapsuleRadius, "avatar", localID, val); break; - case "avatarcapsuleheight": UpdateParameterAvatars(ref m_params[0].avatarCapsuleHeight, "avatar", localID, val); break; + case "avatarcapsuleradius": UpdateParameterAvatars(ref m_params[0].avatarCapsuleRadius, "avatar", localID, val); break; + case "avatarcapsuleheight": UpdateParameterAvatars(ref m_params[0].avatarCapsuleHeight, "avatar", localID, val); break; default: ret = false; break; } @@ -816,6 +835,7 @@ public class BSScene : PhysicsScene, IPhysicsParameters bool ret = true; switch (parm.ToLower()) { + case "detailedstats": val = (int)m_detailedStatsStep; break; case "meshlod": val = (float)m_meshLOD; break; case "sculptlod": val = (float)m_sculptLOD; break; case "maxsubstep": val = (float)m_maxSubSteps; break; @@ -835,6 +855,7 @@ public class BSScene : PhysicsScene, IPhysicsParameters case "angularsleepingthreshold": val = m_params[0].angularDamping; break; case "ccdmotionthreshold": val = m_params[0].ccdMotionThreshold; break; case "ccdsweptsphereradius": val = m_params[0].ccdSweptSphereRadius; break; + case "contactprocessingthreshold": val = m_params[0].contactProcessingThreshold; break; case "terrainfriction": val = m_params[0].terrainFriction; break; case "terrainhitfraction": val = m_params[0].terrainHitFraction; break; diff --git a/OpenSim/Region/Physics/BulletSPlugin/BulletSimAPI.cs b/OpenSim/Region/Physics/BulletSPlugin/BulletSimAPI.cs index a610c8d..d12bd7d 100644 --- a/OpenSim/Region/Physics/BulletSPlugin/BulletSimAPI.cs +++ b/OpenSim/Region/Physics/BulletSPlugin/BulletSimAPI.cs @@ -122,6 +122,7 @@ public struct ConfigurationParameters public float angularSleepingThreshold; public float ccdMotionThreshold; public float ccdSweptSphereRadius; + public float contactProcessingThreshold; public float terrainFriction; public float terrainHitFraction; @@ -248,6 +249,9 @@ public static extern RaycastHit RayTest(uint worldID, uint id, Vector3 from, Vec [DllImport("BulletSim", CallingConvention = CallingConvention.Cdecl), SuppressUnmanagedCodeSecurity] public static extern Vector3 RecoverFromPenetration(uint worldID, uint id); +[DllImport("BulletSim", CallingConvention = CallingConvention.Cdecl), SuppressUnmanagedCodeSecurity] +public static extern void DumpBulletStatistics(); + // Log a debug message [UnmanagedFunctionPointer(CallingConvention.Cdecl)] public delegate void DebugLogCallback([MarshalAs(UnmanagedType.LPStr)]string msg); -- cgit v1.1