From 27d6d9f7201304311f9955c14c75e9c42a24cfec Mon Sep 17 00:00:00 2001 From: Andrew Meadows Date: Thu, 2 Apr 2015 15:20:16 -0700 Subject: [PATCH 1/4] add Bullet profiling hooks for physics simulation --- libraries/physics/src/CharacterController.cpp | 9 ++ libraries/physics/src/PhysicsEngine.cpp | 118 ++++++++++-------- libraries/physics/src/PhysicsEngine.h | 4 + .../physics/src/ThreadSafeDynamicsWorld.cpp | 3 + 4 files changed, 81 insertions(+), 53 deletions(-) diff --git a/libraries/physics/src/CharacterController.cpp b/libraries/physics/src/CharacterController.cpp index cdff02820d..1d7a84c177 100755 --- a/libraries/physics/src/CharacterController.cpp +++ b/libraries/physics/src/CharacterController.cpp @@ -257,6 +257,7 @@ btPairCachingGhostObject* CharacterController::getGhostObject() { } bool CharacterController::recoverFromPenetration(btCollisionWorld* collisionWorld) { + BT_PROFILE("recoverFromPenetration"); // Here we must refresh the overlapping paircache as the penetrating movement itself or the // previous recovery iteration might have used setWorldTransform and pushed us into an object // that is not in the previous cache contents from the last timestep, as will happen if we @@ -355,6 +356,7 @@ bool CharacterController::recoverFromPenetration(btCollisionWorld* collisionWorl void CharacterController::scanDown(btCollisionWorld* world) { + BT_PROFILE("scanDown"); // we test with downward raycast and if we don't find floor close enough then turn on "hover" btKinematicClosestNotMeRayResultCallback callback(_ghostObject); callback.m_collisionFilterGroup = getGhostObject()->getBroadphaseHandle()->m_collisionFilterGroup; @@ -374,6 +376,7 @@ void CharacterController::scanDown(btCollisionWorld* world) { } void CharacterController::stepUp(btCollisionWorld* world) { + BT_PROFILE("stepUp"); // phase 1: up // compute start and end @@ -440,6 +443,7 @@ void CharacterController::updateTargetPositionBasedOnCollision(const btVector3& } void CharacterController::stepForward(btCollisionWorld* collisionWorld, const btVector3& movement) { + BT_PROFILE("stepForward"); // phase 2: forward _targetPosition = _currentPosition + movement; @@ -496,6 +500,7 @@ void CharacterController::stepForward(btCollisionWorld* collisionWorld, const bt } void CharacterController::stepDown(btCollisionWorld* collisionWorld, btScalar dt) { + BT_PROFILE("stepDown"); // phase 3: down // // The "stepDown" phase first makes a normal sweep down that cancels the lift from the "stepUp" phase. @@ -607,6 +612,7 @@ void CharacterController::warp(const btVector3& origin) { void CharacterController::preStep(btCollisionWorld* collisionWorld) { + BT_PROFILE("preStep"); if (!_enabled) { return; } @@ -627,6 +633,7 @@ void CharacterController::preStep(btCollisionWorld* collisionWorld) { } void CharacterController::playerStep(btCollisionWorld* collisionWorld, btScalar dt) { + BT_PROFILE("playerStep"); if (!_enabled) { return; // no motion } @@ -875,6 +882,7 @@ void CharacterController::updateShapeIfNecessary() { } void CharacterController::preSimulation(btScalar timeStep) { + BT_PROFILE("preSimulation"); if (_enabled && _dynamicsWorld) { glm::quat rotation = _avatarData->getOrientation(); _currentUp = quatRotate(glmToBullet(rotation), LOCAL_UP_AXIS); @@ -897,6 +905,7 @@ void CharacterController::preSimulation(btScalar timeStep) { } void CharacterController::postSimulation() { + BT_PROFILE("postSimulation"); if (_enabled && _ghostObject) { const btTransform& avatarTransform = _ghostObject->getWorldTransform(); glm::quat rotation = bulletToGLM(avatarTransform.getRotation()); diff --git a/libraries/physics/src/PhysicsEngine.cpp b/libraries/physics/src/PhysicsEngine.cpp index 467b51560f..67e4e0616f 100644 --- a/libraries/physics/src/PhysicsEngine.cpp +++ b/libraries/physics/src/PhysicsEngine.cpp @@ -155,6 +155,7 @@ void PhysicsEngine::clearEntitiesInternal() { // end EntitySimulation overrides void PhysicsEngine::relayIncomingChangesToSimulation() { + BT_PROFILE("incomingChanges"); // process incoming changes QSet::iterator stateItr = _incomingChanges.begin(); while (stateItr != _incomingChanges.end()) { @@ -287,66 +288,76 @@ void PhysicsEngine::init(EntityEditPacketSender* packetSender) { } void PhysicsEngine::stepSimulation() { - lock(); - // NOTE: the grand order of operations is: - // (1) pull incoming changes - // (2) step simulation - // (3) synchronize outgoing motion states - // (4) send outgoing packets - - // This is step (1) pull incoming changes - relayIncomingChangesToSimulation(); - - const int MAX_NUM_SUBSTEPS = 4; - const float MAX_TIMESTEP = (float)MAX_NUM_SUBSTEPS * PHYSICS_ENGINE_FIXED_SUBSTEP; - float dt = 1.0e-6f * (float)(_clock.getTimeMicroseconds()); - _clock.reset(); - float timeStep = btMin(dt, MAX_TIMESTEP); - - // TODO: move character->preSimulation() into relayIncomingChanges - if (_characterController) { - if (_characterController->needsRemoval()) { - _characterController->setDynamicsWorld(NULL); - } - _characterController->updateShapeIfNecessary(); - if (_characterController->needsAddition()) { - _characterController->setDynamicsWorld(_dynamicsWorld); - } - _characterController->preSimulation(timeStep); - } - - // This is step (2) step simulation - int numSubsteps = _dynamicsWorld->stepSimulation(timeStep, MAX_NUM_SUBSTEPS, PHYSICS_ENGINE_FIXED_SUBSTEP); - _numSubsteps += (uint32_t)numSubsteps; - stepNonPhysicalKinematics(usecTimestampNow()); - unlock(); - - // TODO: make all of this harvest stuff into one function: relayOutgoingChanges() - if (numSubsteps > 0) { - // This is step (3) which is done outside of stepSimulation() so we can lock _entityTree. - // - // Unfortunately we have to unlock the simulation (above) before we try to lock the _entityTree - // to avoid deadlock -- the _entityTree may try to lock its EntitySimulation (from which this - // PhysicsEngine derives) when updating/adding/deleting entities so we need to wait for our own - // lock on the tree before we re-lock ourselves. - // - // TODO: untangle these lock sequences. - _entityTree->lockForWrite(); + { lock(); - _dynamicsWorld->synchronizeMotionStates(); + CProfileManager::Reset(); + BT_PROFILE("stepSimulation"); + // NOTE: the grand order of operations is: + // (1) pull incoming changes + // (2) step simulation + // (3) synchronize outgoing motion states + // (4) send outgoing packets - if (_characterController) { - _characterController->postSimulation(); - } - - unlock(); - _entityTree->unlock(); + // This is step (1) pull incoming changes + relayIncomingChangesToSimulation(); - computeCollisionEvents(); + const int MAX_NUM_SUBSTEPS = 4; + const float MAX_TIMESTEP = (float)MAX_NUM_SUBSTEPS * PHYSICS_ENGINE_FIXED_SUBSTEP; + float dt = 1.0e-6f * (float)(_clock.getTimeMicroseconds()); + _clock.reset(); + float timeStep = btMin(dt, MAX_TIMESTEP); + + // TODO: move character->preSimulation() into relayIncomingChanges + if (_characterController) { + if (_characterController->needsRemoval()) { + _characterController->setDynamicsWorld(NULL); + } + _characterController->updateShapeIfNecessary(); + if (_characterController->needsAddition()) { + _characterController->setDynamicsWorld(_dynamicsWorld); + } + _characterController->preSimulation(timeStep); + } + + // This is step (2) step simulation + int numSubsteps = _dynamicsWorld->stepSimulation(timeStep, MAX_NUM_SUBSTEPS, PHYSICS_ENGINE_FIXED_SUBSTEP); + _numSubsteps += (uint32_t)numSubsteps; + stepNonPhysicalKinematics(usecTimestampNow()); + unlock(); + + // TODO: make all of this harvest stuff into one function: relayOutgoingChanges() + if (numSubsteps > 0) { + BT_PROFILE("postSimulation"); + // This is step (3) which is done outside of stepSimulation() so we can lock _entityTree. + // + // Unfortunately we have to unlock the simulation (above) before we try to lock the _entityTree + // to avoid deadlock -- the _entityTree may try to lock its EntitySimulation (from which this + // PhysicsEngine derives) when updating/adding/deleting entities so we need to wait for our own + // lock on the tree before we re-lock ourselves. + // + // TODO: untangle these lock sequences. + _entityTree->lockForWrite(); + lock(); + _dynamicsWorld->synchronizeMotionStates(); + + if (_characterController) { + _characterController->postSimulation(); + } + + unlock(); + _entityTree->unlock(); + + computeCollisionEvents(); + } + } + if (_dumpNextStats) { + _dumpNextStats = false; + CProfileManager::dumpAll(); } } void PhysicsEngine::stepNonPhysicalKinematics(const quint64& now) { + BT_PROFILE("nonPhysicalKinematics"); QSet::iterator stateItr = _nonPhysicalKinematicObjects.begin(); while (stateItr != _nonPhysicalKinematicObjects.end()) { ObjectMotionState* motionState = *stateItr; @@ -358,6 +369,7 @@ void PhysicsEngine::stepNonPhysicalKinematics(const quint64& now) { // TODO?: need to occasionally scan for stopped non-physical kinematics objects void PhysicsEngine::computeCollisionEvents() { + BT_PROFILE("computeCollisionEvents"); // update all contacts every frame int numManifolds = _collisionDispatcher->getNumManifolds(); for (int i = 0; i < numManifolds; ++i) { diff --git a/libraries/physics/src/PhysicsEngine.h b/libraries/physics/src/PhysicsEngine.h index 0661b47d3a..d7d3278286 100644 --- a/libraries/physics/src/PhysicsEngine.h +++ b/libraries/physics/src/PhysicsEngine.h @@ -86,6 +86,8 @@ public: void setCharacterController(CharacterController* character); + void dumpNextStats() { _dumpNextStats = true; } + private: /// \param motionState pointer to Object's MotionState void removeObjectFromBullet(ObjectMotionState* motionState); @@ -121,6 +123,8 @@ private: /// character collisions CharacterController* _characterController = NULL; + + bool _dumpNextStats = false; }; #endif // hifi_PhysicsEngine_h diff --git a/libraries/physics/src/ThreadSafeDynamicsWorld.cpp b/libraries/physics/src/ThreadSafeDynamicsWorld.cpp index c7ee9ce2a0..b47269183d 100644 --- a/libraries/physics/src/ThreadSafeDynamicsWorld.cpp +++ b/libraries/physics/src/ThreadSafeDynamicsWorld.cpp @@ -15,6 +15,8 @@ * Copied and modified from btDiscreteDynamicsWorld.cpp by AndrewMeadows on 2014.11.12. * */ +#include + #include "ThreadSafeDynamicsWorld.h" ThreadSafeDynamicsWorld::ThreadSafeDynamicsWorld( @@ -26,6 +28,7 @@ ThreadSafeDynamicsWorld::ThreadSafeDynamicsWorld( } int ThreadSafeDynamicsWorld::stepSimulation( btScalar timeStep, int maxSubSteps, btScalar fixedTimeStep) { + BT_PROFILE("stepSimulation"); int subSteps = 0; if (maxSubSteps) { //fixed timestep with interpolation From eaef08abd50e3d54e2fd147c6518f8223bcf70dc Mon Sep 17 00:00:00 2001 From: Andrew Meadows Date: Thu, 2 Apr 2015 15:25:55 -0700 Subject: [PATCH 2/4] add break to Key_Comma switch case --- interface/src/Application.cpp | 1 + 1 file changed, 1 insertion(+) diff --git a/interface/src/Application.cpp b/interface/src/Application.cpp index b19a7850c2..b2aeac3038 100644 --- a/interface/src/Application.cpp +++ b/interface/src/Application.cpp @@ -1182,6 +1182,7 @@ void Application::keyPressEvent(QKeyEvent* event) { case Qt::Key_Comma: { renderCollisionHulls = !renderCollisionHulls; + break; } default: From 2089a8c2dd12220544cfe0312a260af5e1bd3e15 Mon Sep 17 00:00:00 2001 From: Andrew Meadows Date: Thu, 2 Apr 2015 15:33:06 -0700 Subject: [PATCH 3/4] temporary debug setup: dump physics stats on F key --- interface/src/Application.cpp | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/interface/src/Application.cpp b/interface/src/Application.cpp index b2aeac3038..c30ac0cfb0 100644 --- a/interface/src/Application.cpp +++ b/interface/src/Application.cpp @@ -975,6 +975,11 @@ void Application::keyPressEvent(QKeyEvent* event) { _myAvatar->setDriveKeys(UP, 1.0f); break; + case Qt::Key_F: { + _physicsEngine.dumpNextStats(); + break; + } + case Qt::Key_Asterisk: Menu::getInstance()->triggerOption(MenuOption::Stars); break; From 568050686db413f8dd6b2b8d7623e46b0a09fdcc Mon Sep 17 00:00:00 2001 From: Andrew Meadows Date: Sat, 4 Apr 2015 13:50:16 -0700 Subject: [PATCH 4/4] replace tabs with spaces for indentation --- .../physics/src/ThreadSafeDynamicsWorld.cpp | 82 +++++++++---------- 1 file changed, 41 insertions(+), 41 deletions(-) diff --git a/libraries/physics/src/ThreadSafeDynamicsWorld.cpp b/libraries/physics/src/ThreadSafeDynamicsWorld.cpp index b47269183d..a345b915db 100644 --- a/libraries/physics/src/ThreadSafeDynamicsWorld.cpp +++ b/libraries/physics/src/ThreadSafeDynamicsWorld.cpp @@ -27,51 +27,51 @@ ThreadSafeDynamicsWorld::ThreadSafeDynamicsWorld( : btDiscreteDynamicsWorld(dispatcher, pairCache, constraintSolver, collisionConfiguration) { } -int ThreadSafeDynamicsWorld::stepSimulation( btScalar timeStep, int maxSubSteps, btScalar fixedTimeStep) { +int ThreadSafeDynamicsWorld::stepSimulation( btScalar timeStep, int maxSubSteps, btScalar fixedTimeStep) { BT_PROFILE("stepSimulation"); - int subSteps = 0; - if (maxSubSteps) { - //fixed timestep with interpolation - m_fixedTimeStep = fixedTimeStep; - m_localTime += timeStep; - if (m_localTime >= fixedTimeStep) - { - subSteps = int( m_localTime / fixedTimeStep); - m_localTime -= subSteps * fixedTimeStep; - } - } else { - //variable timestep - fixedTimeStep = timeStep; - m_localTime = m_latencyMotionStateInterpolation ? 0 : timeStep; - m_fixedTimeStep = 0; - if (btFuzzyZero(timeStep)) - { - subSteps = 0; - maxSubSteps = 0; - } else - { - subSteps = 1; - maxSubSteps = 1; - } - } + int subSteps = 0; + if (maxSubSteps) { + //fixed timestep with interpolation + m_fixedTimeStep = fixedTimeStep; + m_localTime += timeStep; + if (m_localTime >= fixedTimeStep) + { + subSteps = int( m_localTime / fixedTimeStep); + m_localTime -= subSteps * fixedTimeStep; + } + } else { + //variable timestep + fixedTimeStep = timeStep; + m_localTime = m_latencyMotionStateInterpolation ? 0 : timeStep; + m_fixedTimeStep = 0; + if (btFuzzyZero(timeStep)) + { + subSteps = 0; + maxSubSteps = 0; + } else + { + subSteps = 1; + maxSubSteps = 1; + } + } - /*//process some debugging flags - if (getDebugDrawer()) { - btIDebugDraw* debugDrawer = getDebugDrawer (); - gDisableDeactivation = (debugDrawer->getDebugMode() & btIDebugDraw::DBG_NoDeactivation) != 0; - }*/ - if (subSteps) { - //clamp the number of substeps, to prevent simulation grinding spiralling down to a halt - int clampedSimulationSteps = (subSteps > maxSubSteps)? maxSubSteps : subSteps; + /*//process some debugging flags + if (getDebugDrawer()) { + btIDebugDraw* debugDrawer = getDebugDrawer (); + gDisableDeactivation = (debugDrawer->getDebugMode() & btIDebugDraw::DBG_NoDeactivation) != 0; + }*/ + if (subSteps) { + //clamp the number of substeps, to prevent simulation grinding spiralling down to a halt + int clampedSimulationSteps = (subSteps > maxSubSteps)? maxSubSteps : subSteps; - saveKinematicState(fixedTimeStep*clampedSimulationSteps); + saveKinematicState(fixedTimeStep*clampedSimulationSteps); - applyGravity(); + applyGravity(); - for (int i=0;i