2 Bullet Continuous Collision Detection and Physics Library
3 Copyright (c) 2003-2006 Erwin Coumans https://bulletphysics.org
5 This software is provided 'as-is', without any express or implied warranty.
6 In no event will the authors be held liable for any damages arising from the use of this software.
7 Permission is granted to anyone to use this software for any purpose,
8 including commercial applications, and to alter it and redistribute it freely,
9 subject to the following restrictions:
11 1. The origin of this software must not be misrepresented; you must not claim that you wrote the original software. If you use this software in a product, an acknowledgment in the product documentation would be appreciated but is not required.
12 2. Altered source versions must be plainly marked as such, and must not be misrepresented as being the original software.
13 3. This notice may not be removed or altered from any source distribution.
16 #include "LinearMath/btScalar.h"
17 #include "LinearMath/btThreads.h"
18 #include "btSimulationIslandManagerMt.h"
19 #include "BulletCollision/BroadphaseCollision/btDispatcher.h"
20 #include "BulletCollision/NarrowPhaseCollision/btPersistentManifold.h"
21 #include "BulletCollision/CollisionDispatch/btCollisionObject.h"
22 #include "BulletCollision/CollisionDispatch/btCollisionWorld.h"
23 #include "BulletDynamics/ConstraintSolver/btTypedConstraint.h"
24 #include "BulletDynamics/ConstraintSolver/btSequentialImpulseConstraintSolverMt.h" // for s_minimumContactManifoldsForBatching
27 #include "LinearMath/btQuickprof.h"
29 SIMD_FORCE_INLINE int calcBatchCost(int bodies, int manifolds, int constraints)
31 // rough estimate of the cost of a batch, used for merging
32 int batchCost = bodies + 8 * manifolds + 4 * constraints;
36 SIMD_FORCE_INLINE int calcBatchCost(const btSimulationIslandManagerMt::Island* island)
38 return calcBatchCost(island->bodyArray.size(), island->manifoldArray.size(), island->constraintArray.size());
41 btSimulationIslandManagerMt::btSimulationIslandManagerMt()
43 m_minimumSolverBatchSize = calcBatchCost(0, 128, 0);
44 m_batchIslandMinBodyCount = 32;
45 m_islandDispatch = parallelIslandDispatch;
49 btSimulationIslandManagerMt::~btSimulationIslandManagerMt()
51 for (int i = 0; i < m_allocatedIslands.size(); ++i)
53 delete m_allocatedIslands[i];
55 m_allocatedIslands.resize(0);
56 m_activeIslands.resize(0);
57 m_freeIslands.resize(0);
60 inline int getIslandId(const btPersistentManifold* lhs)
62 const btCollisionObject* rcolObj0 = static_cast<const btCollisionObject*>(lhs->getBody0());
63 const btCollisionObject* rcolObj1 = static_cast<const btCollisionObject*>(lhs->getBody1());
64 int islandId = rcolObj0->getIslandTag() >= 0 ? rcolObj0->getIslandTag() : rcolObj1->getIslandTag();
68 SIMD_FORCE_INLINE int btGetConstraintIslandId1(const btTypedConstraint* lhs)
70 const btCollisionObject& rcolObj0 = lhs->getRigidBodyA();
71 const btCollisionObject& rcolObj1 = lhs->getRigidBodyB();
72 int islandId = rcolObj0.getIslandTag() >= 0 ? rcolObj0.getIslandTag() : rcolObj1.getIslandTag();
76 /// function object that routes calls to operator<
77 class IslandBatchSizeSortPredicate
80 bool operator()(const btSimulationIslandManagerMt::Island* lhs, const btSimulationIslandManagerMt::Island* rhs) const
82 int lCost = calcBatchCost(lhs);
83 int rCost = calcBatchCost(rhs);
88 class IslandBodyCapacitySortPredicate
91 bool operator()(const btSimulationIslandManagerMt::Island* lhs, const btSimulationIslandManagerMt::Island* rhs) const
93 return lhs->bodyArray.capacity() > rhs->bodyArray.capacity();
97 void btSimulationIslandManagerMt::Island::append(const Island& other)
100 for (int i = 0; i < other.bodyArray.size(); ++i)
102 bodyArray.push_back(other.bodyArray[i]);
105 for (int i = 0; i < other.manifoldArray.size(); ++i)
107 manifoldArray.push_back(other.manifoldArray[i]);
109 // append constraints
110 for (int i = 0; i < other.constraintArray.size(); ++i)
112 constraintArray.push_back(other.constraintArray[i]);
116 bool btIsBodyInIsland(const btSimulationIslandManagerMt::Island& island, const btCollisionObject* obj)
118 for (int i = 0; i < island.bodyArray.size(); ++i)
120 if (island.bodyArray[i] == obj)
128 void btSimulationIslandManagerMt::initIslandPools()
130 // reset island pools
131 int numElem = getUnionFind().getNumElements();
132 m_lookupIslandFromId.resize(numElem);
133 for (int i = 0; i < m_lookupIslandFromId.size(); ++i)
135 m_lookupIslandFromId[i] = NULL;
137 m_activeIslands.resize(0);
138 m_freeIslands.resize(0);
139 // check whether allocated islands are sorted by body capacity (largest to smallest)
140 int lastCapacity = 0;
141 bool isSorted = true;
142 for (int i = 0; i < m_allocatedIslands.size(); ++i)
144 Island* island = m_allocatedIslands[i];
145 int cap = island->bodyArray.capacity();
146 if (cap > lastCapacity)
155 m_allocatedIslands.quickSort(IslandBodyCapacitySortPredicate());
158 m_batchIsland = NULL;
159 // mark all islands free (but avoid deallocation)
160 for (int i = 0; i < m_allocatedIslands.size(); ++i)
162 Island* island = m_allocatedIslands[i];
163 island->bodyArray.resize(0);
164 island->manifoldArray.resize(0);
165 island->constraintArray.resize(0);
167 island->isSleeping = true;
168 m_freeIslands.push_back(island);
172 btSimulationIslandManagerMt::Island* btSimulationIslandManagerMt::getIsland(int id)
175 btAssert(id < m_lookupIslandFromId.size());
176 Island* island = m_lookupIslandFromId[id];
179 // search for existing island
180 for (int i = 0; i < m_activeIslands.size(); ++i)
182 if (m_activeIslands[i]->id == id)
184 island = m_activeIslands[i];
188 m_lookupIslandFromId[id] = island;
193 btSimulationIslandManagerMt::Island* btSimulationIslandManagerMt::allocateIsland(int id, int numBodies)
195 Island* island = NULL;
196 int allocSize = numBodies;
197 if (numBodies < m_batchIslandMinBodyCount)
201 island = m_batchIsland;
202 m_lookupIslandFromId[id] = island;
203 // if we've made a large enough batch,
204 if (island->bodyArray.size() + numBodies >= m_batchIslandMinBodyCount)
206 // next time start a new batch
207 m_batchIsland = NULL;
213 // need to allocate a batch island
214 allocSize = m_batchIslandMinBodyCount * 2;
217 btAlignedObjectArray<Island*>& freeIslands = m_freeIslands;
219 // search for free island
220 if (freeIslands.size() > 0)
222 // try to reuse a previously allocated island
223 int iFound = freeIslands.size();
224 // linear search for smallest island that can hold our bodies
225 for (int i = freeIslands.size() - 1; i >= 0; --i)
227 if (freeIslands[i]->bodyArray.capacity() >= allocSize)
230 island = freeIslands[i];
235 // if found, shrink array while maintaining ordering
239 int iSrc = iDest + 1;
240 while (iSrc < freeIslands.size())
242 freeIslands[iDest++] = freeIslands[iSrc++];
244 freeIslands.pop_back();
249 // no free island found, allocate
250 island = new Island(); // TODO: change this to use the pool allocator
252 island->bodyArray.reserve(allocSize);
253 m_allocatedIslands.push_back(island);
255 m_lookupIslandFromId[id] = island;
256 if (numBodies < m_batchIslandMinBodyCount)
258 m_batchIsland = island;
260 m_activeIslands.push_back(island);
264 void btSimulationIslandManagerMt::buildIslands(btDispatcher* dispatcher, btCollisionWorld* collisionWorld)
266 BT_PROFILE("buildIslands");
268 btCollisionObjectArray& collisionObjects = collisionWorld->getCollisionObjectArray();
270 //we are going to sort the unionfind array, and store the element id in the size
271 //afterwards, we clean unionfind, to make sure no-one uses it anymore
273 getUnionFind().sortIslands();
274 int numElem = getUnionFind().getNumElements();
276 int endIslandIndex = 1;
277 int startIslandIndex;
279 //update the sleeping state for bodies, if all are sleeping
280 for (startIslandIndex = 0; startIslandIndex < numElem; startIslandIndex = endIslandIndex)
282 int islandId = getUnionFind().getElement(startIslandIndex).m_id;
283 for (endIslandIndex = startIslandIndex + 1; (endIslandIndex < numElem) && (getUnionFind().getElement(endIslandIndex).m_id == islandId); endIslandIndex++)
287 //int numSleeping = 0;
289 bool allSleeping = true;
292 for (idx = startIslandIndex; idx < endIslandIndex; idx++)
294 int i = getUnionFind().getElement(idx).m_sz;
296 btCollisionObject* colObj0 = collisionObjects[i];
297 if ((colObj0->getIslandTag() != islandId) && (colObj0->getIslandTag() != -1))
299 // printf("error in island management\n");
302 btAssert((colObj0->getIslandTag() == islandId) || (colObj0->getIslandTag() == -1));
303 if (colObj0->getIslandTag() == islandId)
305 if (colObj0->getActivationState() == ACTIVE_TAG ||
306 colObj0->getActivationState() == DISABLE_DEACTIVATION)
317 for (idx = startIslandIndex; idx < endIslandIndex; idx++)
319 int i = getUnionFind().getElement(idx).m_sz;
320 btCollisionObject* colObj0 = collisionObjects[i];
321 if ((colObj0->getIslandTag() != islandId) && (colObj0->getIslandTag() != -1))
323 // printf("error in island management\n");
326 btAssert((colObj0->getIslandTag() == islandId) || (colObj0->getIslandTag() == -1));
328 if (colObj0->getIslandTag() == islandId)
330 colObj0->setActivationState(ISLAND_SLEEPING);
337 for (idx = startIslandIndex; idx < endIslandIndex; idx++)
339 int i = getUnionFind().getElement(idx).m_sz;
341 btCollisionObject* colObj0 = collisionObjects[i];
342 if ((colObj0->getIslandTag() != islandId) && (colObj0->getIslandTag() != -1))
344 // printf("error in island management\n");
347 btAssert((colObj0->getIslandTag() == islandId) || (colObj0->getIslandTag() == -1));
349 if (colObj0->getIslandTag() == islandId)
351 if (colObj0->getActivationState() == ISLAND_SLEEPING)
353 colObj0->setActivationState(WANTS_DEACTIVATION);
354 colObj0->setDeactivationTime(0.f);
362 void btSimulationIslandManagerMt::addBodiesToIslands(btCollisionWorld* collisionWorld)
364 btCollisionObjectArray& collisionObjects = collisionWorld->getCollisionObjectArray();
365 int endIslandIndex = 1;
366 int startIslandIndex;
367 int numElem = getUnionFind().getNumElements();
369 // create explicit islands and add bodies to each
370 for (startIslandIndex = 0; startIslandIndex < numElem; startIslandIndex = endIslandIndex)
372 int islandId = getUnionFind().getElement(startIslandIndex).m_id;
375 for (endIslandIndex = startIslandIndex; (endIslandIndex < numElem) && (getUnionFind().getElement(endIslandIndex).m_id == islandId); endIslandIndex++)
378 // check if island is sleeping
379 bool islandSleeping = true;
380 for (int iElem = startIslandIndex; iElem < endIslandIndex; iElem++)
382 int i = getUnionFind().getElement(iElem).m_sz;
383 btCollisionObject* colObj = collisionObjects[i];
384 if (colObj->isActive())
386 islandSleeping = false;
391 // want to count the number of bodies before allocating the island to optimize memory usage of the Island structures
392 int numBodies = endIslandIndex - startIslandIndex;
393 Island* island = allocateIsland(islandId, numBodies);
394 island->isSleeping = false;
396 // add bodies to island
397 for (int iElem = startIslandIndex; iElem < endIslandIndex; iElem++)
399 int i = getUnionFind().getElement(iElem).m_sz;
400 btCollisionObject* colObj = collisionObjects[i];
401 island->bodyArray.push_back(colObj);
407 void btSimulationIslandManagerMt::addManifoldsToIslands(btDispatcher* dispatcher)
409 // walk all the manifolds, activating bodies touched by kinematic objects, and add each manifold to its Island
410 int maxNumManifolds = dispatcher->getNumManifolds();
411 for (int i = 0; i < maxNumManifolds; i++)
413 btPersistentManifold* manifold = dispatcher->getManifoldByIndexInternal(i);
415 const btCollisionObject* colObj0 = static_cast<const btCollisionObject*>(manifold->getBody0());
416 const btCollisionObject* colObj1 = static_cast<const btCollisionObject*>(manifold->getBody1());
418 ///@todo: check sleeping conditions!
419 if (((colObj0) && colObj0->getActivationState() != ISLAND_SLEEPING) ||
420 ((colObj1) && colObj1->getActivationState() != ISLAND_SLEEPING))
422 //kinematic objects don't merge islands, but wake up all connected objects
423 if (colObj0->isKinematicObject() && colObj0->getActivationState() != ISLAND_SLEEPING)
425 if (colObj0->hasContactResponse())
428 if (colObj1->isKinematicObject() && colObj1->getActivationState() != ISLAND_SLEEPING)
430 if (colObj1->hasContactResponse())
433 //filtering for response
434 if (dispatcher->needsResponse(colObj0, colObj1))
436 // scatter manifolds into various islands
437 int islandId = getIslandId(manifold);
438 // if island not sleeping,
439 if (Island* island = getIsland(islandId))
441 island->manifoldArray.push_back(manifold);
448 void btSimulationIslandManagerMt::addConstraintsToIslands(btAlignedObjectArray<btTypedConstraint*>& constraints)
451 for (int i = 0; i < constraints.size(); i++)
453 // scatter constraints into various islands
454 btTypedConstraint* constraint = constraints[i];
455 if (constraint->isEnabled())
457 int islandId = btGetConstraintIslandId1(constraint);
458 // if island is not sleeping,
459 if (Island* island = getIsland(islandId))
461 island->constraintArray.push_back(constraint);
467 void btSimulationIslandManagerMt::mergeIslands()
469 // sort islands in order of decreasing batch size
470 m_activeIslands.quickSort(IslandBatchSizeSortPredicate());
472 // merge small islands to satisfy minimum batch size
473 // find first small batch island
474 int destIslandIndex = m_activeIslands.size();
475 for (int i = 0; i < m_activeIslands.size(); ++i)
477 Island* island = m_activeIslands[i];
478 int batchSize = calcBatchCost(island);
479 if (batchSize < m_minimumSolverBatchSize)
485 int lastIndex = m_activeIslands.size() - 1;
486 while (destIslandIndex < lastIndex)
488 // merge islands from the back of the list
489 Island* island = m_activeIslands[destIslandIndex];
490 int numBodies = island->bodyArray.size();
491 int numManifolds = island->manifoldArray.size();
492 int numConstraints = island->constraintArray.size();
493 int firstIndex = lastIndex;
494 // figure out how many islands we want to merge and find out how many bodies, manifolds and constraints we will have
497 Island* src = m_activeIslands[firstIndex];
498 numBodies += src->bodyArray.size();
499 numManifolds += src->manifoldArray.size();
500 numConstraints += src->constraintArray.size();
501 int batchCost = calcBatchCost(numBodies, numManifolds, numConstraints);
502 if (batchCost >= m_minimumSolverBatchSize)
506 if (firstIndex - 1 == destIslandIndex)
512 // reserve space for these pointers to minimize reallocation
513 island->bodyArray.reserve(numBodies);
514 island->manifoldArray.reserve(numManifolds);
515 island->constraintArray.reserve(numConstraints);
517 for (int i = firstIndex; i <= lastIndex; ++i)
519 island->append(*m_activeIslands[i]);
521 // shrink array to exclude the islands that were merged from
522 m_activeIslands.resize(firstIndex);
523 lastIndex = firstIndex - 1;
528 void btSimulationIslandManagerMt::solveIsland(btConstraintSolver* solver, Island& island, const SolverParams& solverParams)
530 btPersistentManifold** manifolds = island.manifoldArray.size() ? &island.manifoldArray[0] : NULL;
531 btTypedConstraint** constraintsPtr = island.constraintArray.size() ? &island.constraintArray[0] : NULL;
532 solver->solveGroup(&island.bodyArray[0],
533 island.bodyArray.size(),
535 island.manifoldArray.size(),
537 island.constraintArray.size(),
538 *solverParams.m_solverInfo,
539 solverParams.m_debugDrawer,
540 solverParams.m_dispatcher);
543 void btSimulationIslandManagerMt::serialIslandDispatch(btAlignedObjectArray<Island*>* islandsPtr, const SolverParams& solverParams)
545 BT_PROFILE("serialIslandDispatch");
547 btAlignedObjectArray<Island*>& islands = *islandsPtr;
548 btConstraintSolver* solver = solverParams.m_solverMt ? solverParams.m_solverMt : solverParams.m_solverPool;
549 for (int i = 0; i < islands.size(); ++i)
551 solveIsland(solver, *islands[i], solverParams);
555 struct UpdateIslandDispatcher : public btIParallelForBody
557 btAlignedObjectArray<btSimulationIslandManagerMt::Island*>& m_islandsPtr;
558 const btSimulationIslandManagerMt::SolverParams& m_solverParams;
560 UpdateIslandDispatcher(btAlignedObjectArray<btSimulationIslandManagerMt::Island*>& islandsPtr, const btSimulationIslandManagerMt::SolverParams& solverParams)
561 : m_islandsPtr(islandsPtr), m_solverParams(solverParams)
565 void forLoop(int iBegin, int iEnd) const BT_OVERRIDE
567 btConstraintSolver* solver = m_solverParams.m_solverPool;
568 for (int i = iBegin; i < iEnd; ++i)
570 btSimulationIslandManagerMt::Island* island = m_islandsPtr[i];
571 btSimulationIslandManagerMt::solveIsland(solver, *island, m_solverParams);
576 void btSimulationIslandManagerMt::parallelIslandDispatch(btAlignedObjectArray<Island*>* islandsPtr, const SolverParams& solverParams)
578 BT_PROFILE("parallelIslandDispatch");
580 // if there are islands with many contacts, it may be faster to submit these
581 // large islands *serially* to a single parallel constraint solver, and then later
582 // submit the remaining smaller islands in parallel to multiple sequential solvers.
584 // Some task schedulers do not deal well with nested parallelFor loops. One implementation
585 // of OpenMP was actually slower than doing everything single-threaded. Intel TBB
586 // on the other hand, seems to do a pretty respectable job with it.
588 // When solving islands in parallel, the worst case performance happens when there
589 // is one very large island and then perhaps a smattering of very small
590 // islands -- one worker thread takes the large island and the remaining workers
591 // tear through the smaller islands and then sit idle waiting for the first worker
592 // to finish. Solving islands in parallel works best when there are numerous small
593 // islands, roughly equal in size.
595 // By contrast, the other approach -- the parallel constraint solver -- is only
596 // able to deliver a worthwhile speedup when the island is large. For smaller islands,
597 // it is difficult to extract a useful amount of parallelism -- the overhead of grouping
598 // the constraints into batches and sending the batches to worker threads can nullify
599 // any gains from parallelism.
602 UpdateIslandDispatcher dispatcher(*islandsPtr, solverParams);
603 // We take advantage of the fact the islands are sorted in order of decreasing size
605 if (solverParams.m_solverMt)
607 while (iBegin < islandsPtr->size())
609 btSimulationIslandManagerMt::Island* island = (*islandsPtr)[iBegin];
610 if (island->manifoldArray.size() < btSequentialImpulseConstraintSolverMt::s_minimumContactManifoldsForBatching)
612 // OK to submit the rest of the array in parallel
615 // serial dispatch to parallel solver for large islands (if any)
616 solveIsland(solverParams.m_solverMt, *island, solverParams);
620 // parallel dispatch to sequential solvers for rest
621 btParallelFor(iBegin, islandsPtr->size(), 1, dispatcher);
624 ///@todo: this is random access, it can be walked 'cache friendly'!
625 void btSimulationIslandManagerMt::buildAndProcessIslands(btDispatcher* dispatcher,
626 btCollisionWorld* collisionWorld,
627 btAlignedObjectArray<btTypedConstraint*>& constraints,
628 const SolverParams& solverParams)
630 BT_PROFILE("buildAndProcessIslands");
631 btCollisionObjectArray& collisionObjects = collisionWorld->getCollisionObjectArray();
633 buildIslands(dispatcher, collisionWorld);
635 if (!getSplitIslands())
637 btPersistentManifold** manifolds = dispatcher->getInternalManifoldPointer();
638 int maxNumManifolds = dispatcher->getNumManifolds();
640 for (int i = 0; i < maxNumManifolds; i++)
642 btPersistentManifold* manifold = manifolds[i];
644 const btCollisionObject* colObj0 = static_cast<const btCollisionObject*>(manifold->getBody0());
645 const btCollisionObject* colObj1 = static_cast<const btCollisionObject*>(manifold->getBody1());
647 ///@todo: check sleeping conditions!
648 if (((colObj0) && colObj0->getActivationState() != ISLAND_SLEEPING) ||
649 ((colObj1) && colObj1->getActivationState() != ISLAND_SLEEPING))
651 //kinematic objects don't merge islands, but wake up all connected objects
652 if (colObj0->isKinematicObject() && colObj0->getActivationState() != ISLAND_SLEEPING)
654 if (colObj0->hasContactResponse())
657 if (colObj1->isKinematicObject() && colObj1->getActivationState() != ISLAND_SLEEPING)
659 if (colObj1->hasContactResponse())
664 btTypedConstraint** constraintsPtr = constraints.size() ? &constraints[0] : NULL;
665 btConstraintSolver* solver = solverParams.m_solverMt ? solverParams.m_solverMt : solverParams.m_solverPool;
666 solver->solveGroup(&collisionObjects[0],
667 collisionObjects.size(),
672 *solverParams.m_solverInfo,
673 solverParams.m_debugDrawer,
674 solverParams.m_dispatcher);
680 //traverse the simulation islands, and call the solver, unless all objects are sleeping/deactivated
681 addBodiesToIslands(collisionWorld);
682 addManifoldsToIslands(dispatcher);
683 addConstraintsToIslands(constraints);
685 // m_activeIslands array should now contain all non-sleeping Islands, and each Island should
686 // have all the necessary bodies, manifolds and constraints.
688 // if we want to merge islands with small batch counts,
689 if (m_minimumSolverBatchSize > 1)
693 // dispatch islands to solver
694 m_islandDispatch(&m_activeIslands, solverParams);