2017-08-01 14:30:58 +02:00
|
|
|
/*
|
|
|
|
Bullet Continuous Collision Detection and Physics Library
|
|
|
|
Copyright (c) 2003-2006 Erwin Coumans http://continuousphysics.com/Bullet/
|
|
|
|
|
|
|
|
This software is provided 'as-is', without any express or implied warranty.
|
|
|
|
In no event will the authors be held liable for any damages arising from the use of this software.
|
|
|
|
Permission is granted to anyone to use this software for any purpose,
|
|
|
|
including commercial applications, and to alter it and redistribute it freely,
|
|
|
|
subject to the following restrictions:
|
|
|
|
|
|
|
|
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.
|
|
|
|
2. Altered source versions must be plainly marked as such, and must not be misrepresented as being the original software.
|
|
|
|
3. This notice may not be removed or altered from any source distribution.
|
|
|
|
*/
|
|
|
|
|
|
|
|
#include "LinearMath/btScalar.h"
|
|
|
|
#include "LinearMath/btThreads.h"
|
|
|
|
#include "btSimulationIslandManagerMt.h"
|
|
|
|
#include "BulletCollision/BroadphaseCollision/btDispatcher.h"
|
|
|
|
#include "BulletCollision/NarrowPhaseCollision/btPersistentManifold.h"
|
|
|
|
#include "BulletCollision/CollisionDispatch/btCollisionObject.h"
|
|
|
|
#include "BulletCollision/CollisionDispatch/btCollisionWorld.h"
|
|
|
|
#include "BulletDynamics/ConstraintSolver/btTypedConstraint.h"
|
2018-09-07 16:11:04 +02:00
|
|
|
#include "BulletDynamics/ConstraintSolver/btSequentialImpulseConstraintSolverMt.h" // for s_minimumContactManifoldsForBatching
|
2017-08-01 14:30:58 +02:00
|
|
|
|
|
|
|
//#include <stdio.h>
|
|
|
|
#include "LinearMath/btQuickprof.h"
|
|
|
|
|
2019-01-03 14:26:51 +01:00
|
|
|
SIMD_FORCE_INLINE int calcBatchCost(int bodies, int manifolds, int constraints)
|
2017-08-01 14:30:58 +02:00
|
|
|
{
|
2019-01-03 14:26:51 +01:00
|
|
|
// rough estimate of the cost of a batch, used for merging
|
|
|
|
int batchCost = bodies + 8 * manifolds + 4 * constraints;
|
|
|
|
return batchCost;
|
2017-08-01 14:30:58 +02:00
|
|
|
}
|
|
|
|
|
2019-01-03 14:26:51 +01:00
|
|
|
SIMD_FORCE_INLINE int calcBatchCost(const btSimulationIslandManagerMt::Island* island)
|
2017-08-01 14:30:58 +02:00
|
|
|
{
|
2019-01-03 14:26:51 +01:00
|
|
|
return calcBatchCost(island->bodyArray.size(), island->manifoldArray.size(), island->constraintArray.size());
|
2017-08-01 14:30:58 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
btSimulationIslandManagerMt::btSimulationIslandManagerMt()
|
|
|
|
{
|
2019-01-03 14:26:51 +01:00
|
|
|
m_minimumSolverBatchSize = calcBatchCost(0, 128, 0);
|
|
|
|
m_batchIslandMinBodyCount = 32;
|
|
|
|
m_islandDispatch = parallelIslandDispatch;
|
|
|
|
m_batchIsland = NULL;
|
2017-08-01 14:30:58 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
btSimulationIslandManagerMt::~btSimulationIslandManagerMt()
|
|
|
|
{
|
2019-01-03 14:26:51 +01:00
|
|
|
for (int i = 0; i < m_allocatedIslands.size(); ++i)
|
|
|
|
{
|
|
|
|
delete m_allocatedIslands[i];
|
|
|
|
}
|
|
|
|
m_allocatedIslands.resize(0);
|
|
|
|
m_activeIslands.resize(0);
|
|
|
|
m_freeIslands.resize(0);
|
2017-08-01 14:30:58 +02:00
|
|
|
}
|
|
|
|
|
2019-01-03 14:26:51 +01:00
|
|
|
inline int getIslandId(const btPersistentManifold* lhs)
|
2017-08-01 14:30:58 +02:00
|
|
|
{
|
|
|
|
const btCollisionObject* rcolObj0 = static_cast<const btCollisionObject*>(lhs->getBody0());
|
|
|
|
const btCollisionObject* rcolObj1 = static_cast<const btCollisionObject*>(lhs->getBody1());
|
2019-01-03 14:26:51 +01:00
|
|
|
int islandId = rcolObj0->getIslandTag() >= 0 ? rcolObj0->getIslandTag() : rcolObj1->getIslandTag();
|
2017-08-01 14:30:58 +02:00
|
|
|
return islandId;
|
|
|
|
}
|
|
|
|
|
2019-06-11 13:18:05 +02:00
|
|
|
SIMD_FORCE_INLINE int btGetConstraintIslandId1(const btTypedConstraint* lhs)
|
2017-08-01 14:30:58 +02:00
|
|
|
{
|
2019-01-03 14:26:51 +01:00
|
|
|
const btCollisionObject& rcolObj0 = lhs->getRigidBodyA();
|
|
|
|
const btCollisionObject& rcolObj1 = lhs->getRigidBodyB();
|
|
|
|
int islandId = rcolObj0.getIslandTag() >= 0 ? rcolObj0.getIslandTag() : rcolObj1.getIslandTag();
|
|
|
|
return islandId;
|
2017-08-01 14:30:58 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
/// function object that routes calls to operator<
|
|
|
|
class IslandBatchSizeSortPredicate
|
|
|
|
{
|
|
|
|
public:
|
2019-01-03 14:26:51 +01:00
|
|
|
bool operator()(const btSimulationIslandManagerMt::Island* lhs, const btSimulationIslandManagerMt::Island* rhs) const
|
|
|
|
{
|
|
|
|
int lCost = calcBatchCost(lhs);
|
|
|
|
int rCost = calcBatchCost(rhs);
|
|
|
|
return lCost > rCost;
|
|
|
|
}
|
2017-08-01 14:30:58 +02:00
|
|
|
};
|
|
|
|
|
|
|
|
class IslandBodyCapacitySortPredicate
|
|
|
|
{
|
|
|
|
public:
|
2019-01-03 14:26:51 +01:00
|
|
|
bool operator()(const btSimulationIslandManagerMt::Island* lhs, const btSimulationIslandManagerMt::Island* rhs) const
|
|
|
|
{
|
|
|
|
return lhs->bodyArray.capacity() > rhs->bodyArray.capacity();
|
|
|
|
}
|
2017-08-01 14:30:58 +02:00
|
|
|
};
|
|
|
|
|
2019-01-03 14:26:51 +01:00
|
|
|
void btSimulationIslandManagerMt::Island::append(const Island& other)
|
2017-08-01 14:30:58 +02:00
|
|
|
{
|
2019-01-03 14:26:51 +01:00
|
|
|
// append bodies
|
|
|
|
for (int i = 0; i < other.bodyArray.size(); ++i)
|
|
|
|
{
|
|
|
|
bodyArray.push_back(other.bodyArray[i]);
|
|
|
|
}
|
|
|
|
// append manifolds
|
|
|
|
for (int i = 0; i < other.manifoldArray.size(); ++i)
|
|
|
|
{
|
|
|
|
manifoldArray.push_back(other.manifoldArray[i]);
|
|
|
|
}
|
|
|
|
// append constraints
|
|
|
|
for (int i = 0; i < other.constraintArray.size(); ++i)
|
|
|
|
{
|
|
|
|
constraintArray.push_back(other.constraintArray[i]);
|
|
|
|
}
|
2017-08-01 14:30:58 +02:00
|
|
|
}
|
|
|
|
|
2019-01-03 14:26:51 +01:00
|
|
|
bool btIsBodyInIsland(const btSimulationIslandManagerMt::Island& island, const btCollisionObject* obj)
|
2017-08-01 14:30:58 +02:00
|
|
|
{
|
2019-01-03 14:26:51 +01:00
|
|
|
for (int i = 0; i < island.bodyArray.size(); ++i)
|
|
|
|
{
|
|
|
|
if (island.bodyArray[i] == obj)
|
|
|
|
{
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return false;
|
2017-08-01 14:30:58 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
void btSimulationIslandManagerMt::initIslandPools()
|
|
|
|
{
|
2019-01-03 14:26:51 +01:00
|
|
|
// reset island pools
|
|
|
|
int numElem = getUnionFind().getNumElements();
|
|
|
|
m_lookupIslandFromId.resize(numElem);
|
|
|
|
for (int i = 0; i < m_lookupIslandFromId.size(); ++i)
|
|
|
|
{
|
|
|
|
m_lookupIslandFromId[i] = NULL;
|
|
|
|
}
|
|
|
|
m_activeIslands.resize(0);
|
|
|
|
m_freeIslands.resize(0);
|
|
|
|
// check whether allocated islands are sorted by body capacity (largest to smallest)
|
|
|
|
int lastCapacity = 0;
|
|
|
|
bool isSorted = true;
|
|
|
|
for (int i = 0; i < m_allocatedIslands.size(); ++i)
|
|
|
|
{
|
|
|
|
Island* island = m_allocatedIslands[i];
|
|
|
|
int cap = island->bodyArray.capacity();
|
|
|
|
if (cap > lastCapacity)
|
|
|
|
{
|
|
|
|
isSorted = false;
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
lastCapacity = cap;
|
|
|
|
}
|
|
|
|
if (!isSorted)
|
|
|
|
{
|
|
|
|
m_allocatedIslands.quickSort(IslandBodyCapacitySortPredicate());
|
|
|
|
}
|
2017-08-01 14:30:58 +02:00
|
|
|
|
2019-01-03 14:26:51 +01:00
|
|
|
m_batchIsland = NULL;
|
|
|
|
// mark all islands free (but avoid deallocation)
|
|
|
|
for (int i = 0; i < m_allocatedIslands.size(); ++i)
|
|
|
|
{
|
|
|
|
Island* island = m_allocatedIslands[i];
|
|
|
|
island->bodyArray.resize(0);
|
|
|
|
island->manifoldArray.resize(0);
|
|
|
|
island->constraintArray.resize(0);
|
|
|
|
island->id = -1;
|
|
|
|
island->isSleeping = true;
|
|
|
|
m_freeIslands.push_back(island);
|
|
|
|
}
|
|
|
|
}
|
2017-08-01 14:30:58 +02:00
|
|
|
|
2019-01-03 14:26:51 +01:00
|
|
|
btSimulationIslandManagerMt::Island* btSimulationIslandManagerMt::getIsland(int id)
|
2017-08-01 14:30:58 +02:00
|
|
|
{
|
2021-09-29 15:47:08 +02:00
|
|
|
btAssert(id >= 0);
|
|
|
|
btAssert(id < m_lookupIslandFromId.size());
|
2019-01-03 14:26:51 +01:00
|
|
|
Island* island = m_lookupIslandFromId[id];
|
|
|
|
if (island == NULL)
|
|
|
|
{
|
|
|
|
// search for existing island
|
|
|
|
for (int i = 0; i < m_activeIslands.size(); ++i)
|
|
|
|
{
|
|
|
|
if (m_activeIslands[i]->id == id)
|
|
|
|
{
|
|
|
|
island = m_activeIslands[i];
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
m_lookupIslandFromId[id] = island;
|
|
|
|
}
|
|
|
|
return island;
|
2017-08-01 14:30:58 +02:00
|
|
|
}
|
|
|
|
|
2019-01-03 14:26:51 +01:00
|
|
|
btSimulationIslandManagerMt::Island* btSimulationIslandManagerMt::allocateIsland(int id, int numBodies)
|
2017-08-01 14:30:58 +02:00
|
|
|
{
|
2019-01-03 14:26:51 +01:00
|
|
|
Island* island = NULL;
|
|
|
|
int allocSize = numBodies;
|
|
|
|
if (numBodies < m_batchIslandMinBodyCount)
|
|
|
|
{
|
|
|
|
if (m_batchIsland)
|
|
|
|
{
|
|
|
|
island = m_batchIsland;
|
|
|
|
m_lookupIslandFromId[id] = island;
|
|
|
|
// if we've made a large enough batch,
|
|
|
|
if (island->bodyArray.size() + numBodies >= m_batchIslandMinBodyCount)
|
|
|
|
{
|
|
|
|
// next time start a new batch
|
|
|
|
m_batchIsland = NULL;
|
|
|
|
}
|
|
|
|
return island;
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
// need to allocate a batch island
|
|
|
|
allocSize = m_batchIslandMinBodyCount * 2;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
btAlignedObjectArray<Island*>& freeIslands = m_freeIslands;
|
2017-08-01 14:30:58 +02:00
|
|
|
|
2019-01-03 14:26:51 +01:00
|
|
|
// search for free island
|
|
|
|
if (freeIslands.size() > 0)
|
|
|
|
{
|
|
|
|
// try to reuse a previously allocated island
|
|
|
|
int iFound = freeIslands.size();
|
|
|
|
// linear search for smallest island that can hold our bodies
|
|
|
|
for (int i = freeIslands.size() - 1; i >= 0; --i)
|
|
|
|
{
|
|
|
|
if (freeIslands[i]->bodyArray.capacity() >= allocSize)
|
|
|
|
{
|
|
|
|
iFound = i;
|
|
|
|
island = freeIslands[i];
|
|
|
|
island->id = id;
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
// if found, shrink array while maintaining ordering
|
|
|
|
if (island)
|
|
|
|
{
|
|
|
|
int iDest = iFound;
|
|
|
|
int iSrc = iDest + 1;
|
|
|
|
while (iSrc < freeIslands.size())
|
|
|
|
{
|
|
|
|
freeIslands[iDest++] = freeIslands[iSrc++];
|
|
|
|
}
|
|
|
|
freeIslands.pop_back();
|
|
|
|
}
|
|
|
|
}
|
|
|
|
if (island == NULL)
|
|
|
|
{
|
|
|
|
// no free island found, allocate
|
|
|
|
island = new Island(); // TODO: change this to use the pool allocator
|
|
|
|
island->id = id;
|
|
|
|
island->bodyArray.reserve(allocSize);
|
|
|
|
m_allocatedIslands.push_back(island);
|
|
|
|
}
|
|
|
|
m_lookupIslandFromId[id] = island;
|
|
|
|
if (numBodies < m_batchIslandMinBodyCount)
|
|
|
|
{
|
|
|
|
m_batchIsland = island;
|
|
|
|
}
|
|
|
|
m_activeIslands.push_back(island);
|
|
|
|
return island;
|
|
|
|
}
|
2017-08-01 14:30:58 +02:00
|
|
|
|
2019-01-03 14:26:51 +01:00
|
|
|
void btSimulationIslandManagerMt::buildIslands(btDispatcher* dispatcher, btCollisionWorld* collisionWorld)
|
2017-08-01 14:30:58 +02:00
|
|
|
{
|
2018-09-07 16:11:04 +02:00
|
|
|
BT_PROFILE("buildIslands");
|
2019-01-03 14:26:51 +01:00
|
|
|
|
2017-08-01 14:30:58 +02:00
|
|
|
btCollisionObjectArray& collisionObjects = collisionWorld->getCollisionObjectArray();
|
|
|
|
|
|
|
|
//we are going to sort the unionfind array, and store the element id in the size
|
|
|
|
//afterwards, we clean unionfind, to make sure no-one uses it anymore
|
2019-01-03 14:26:51 +01:00
|
|
|
|
2017-08-01 14:30:58 +02:00
|
|
|
getUnionFind().sortIslands();
|
|
|
|
int numElem = getUnionFind().getNumElements();
|
|
|
|
|
2019-01-03 14:26:51 +01:00
|
|
|
int endIslandIndex = 1;
|
2017-08-01 14:30:58 +02:00
|
|
|
int startIslandIndex;
|
|
|
|
|
|
|
|
//update the sleeping state for bodies, if all are sleeping
|
2019-01-03 14:26:51 +01:00
|
|
|
for (startIslandIndex = 0; startIslandIndex < numElem; startIslandIndex = endIslandIndex)
|
2017-08-01 14:30:58 +02:00
|
|
|
{
|
|
|
|
int islandId = getUnionFind().getElement(startIslandIndex).m_id;
|
2019-01-03 14:26:51 +01:00
|
|
|
for (endIslandIndex = startIslandIndex + 1; (endIslandIndex < numElem) && (getUnionFind().getElement(endIslandIndex).m_id == islandId); endIslandIndex++)
|
2017-08-01 14:30:58 +02:00
|
|
|
{
|
|
|
|
}
|
|
|
|
|
|
|
|
//int numSleeping = 0;
|
|
|
|
|
|
|
|
bool allSleeping = true;
|
|
|
|
|
|
|
|
int idx;
|
2019-01-03 14:26:51 +01:00
|
|
|
for (idx = startIslandIndex; idx < endIslandIndex; idx++)
|
2017-08-01 14:30:58 +02:00
|
|
|
{
|
|
|
|
int i = getUnionFind().getElement(idx).m_sz;
|
|
|
|
|
|
|
|
btCollisionObject* colObj0 = collisionObjects[i];
|
|
|
|
if ((colObj0->getIslandTag() != islandId) && (colObj0->getIslandTag() != -1))
|
|
|
|
{
|
2019-01-03 14:26:51 +01:00
|
|
|
// printf("error in island management\n");
|
2017-08-01 14:30:58 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
btAssert((colObj0->getIslandTag() == islandId) || (colObj0->getIslandTag() == -1));
|
|
|
|
if (colObj0->getIslandTag() == islandId)
|
|
|
|
{
|
2019-01-03 14:26:51 +01:00
|
|
|
if (colObj0->getActivationState() == ACTIVE_TAG ||
|
|
|
|
colObj0->getActivationState() == DISABLE_DEACTIVATION)
|
2017-08-01 14:30:58 +02:00
|
|
|
{
|
|
|
|
allSleeping = false;
|
2018-09-07 16:11:04 +02:00
|
|
|
break;
|
2017-08-01 14:30:58 +02:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
if (allSleeping)
|
|
|
|
{
|
|
|
|
int idx;
|
2019-01-03 14:26:51 +01:00
|
|
|
for (idx = startIslandIndex; idx < endIslandIndex; idx++)
|
2017-08-01 14:30:58 +02:00
|
|
|
{
|
|
|
|
int i = getUnionFind().getElement(idx).m_sz;
|
|
|
|
btCollisionObject* colObj0 = collisionObjects[i];
|
|
|
|
if ((colObj0->getIslandTag() != islandId) && (colObj0->getIslandTag() != -1))
|
|
|
|
{
|
2019-01-03 14:26:51 +01:00
|
|
|
// printf("error in island management\n");
|
2017-08-01 14:30:58 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
btAssert((colObj0->getIslandTag() == islandId) || (colObj0->getIslandTag() == -1));
|
|
|
|
|
|
|
|
if (colObj0->getIslandTag() == islandId)
|
|
|
|
{
|
2019-01-03 14:26:51 +01:00
|
|
|
colObj0->setActivationState(ISLAND_SLEEPING);
|
2017-08-01 14:30:58 +02:00
|
|
|
}
|
|
|
|
}
|
2019-01-03 14:26:51 +01:00
|
|
|
}
|
|
|
|
else
|
2017-08-01 14:30:58 +02:00
|
|
|
{
|
|
|
|
int idx;
|
2019-01-03 14:26:51 +01:00
|
|
|
for (idx = startIslandIndex; idx < endIslandIndex; idx++)
|
2017-08-01 14:30:58 +02:00
|
|
|
{
|
|
|
|
int i = getUnionFind().getElement(idx).m_sz;
|
|
|
|
|
|
|
|
btCollisionObject* colObj0 = collisionObjects[i];
|
|
|
|
if ((colObj0->getIslandTag() != islandId) && (colObj0->getIslandTag() != -1))
|
|
|
|
{
|
2019-01-03 14:26:51 +01:00
|
|
|
// printf("error in island management\n");
|
2017-08-01 14:30:58 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
btAssert((colObj0->getIslandTag() == islandId) || (colObj0->getIslandTag() == -1));
|
|
|
|
|
|
|
|
if (colObj0->getIslandTag() == islandId)
|
|
|
|
{
|
2019-01-03 14:26:51 +01:00
|
|
|
if (colObj0->getActivationState() == ISLAND_SLEEPING)
|
2017-08-01 14:30:58 +02:00
|
|
|
{
|
2019-01-03 14:26:51 +01:00
|
|
|
colObj0->setActivationState(WANTS_DEACTIVATION);
|
2017-08-01 14:30:58 +02:00
|
|
|
colObj0->setDeactivationTime(0.f);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2019-01-03 14:26:51 +01:00
|
|
|
void btSimulationIslandManagerMt::addBodiesToIslands(btCollisionWorld* collisionWorld)
|
2017-08-01 14:30:58 +02:00
|
|
|
{
|
2019-01-03 14:26:51 +01:00
|
|
|
btCollisionObjectArray& collisionObjects = collisionWorld->getCollisionObjectArray();
|
|
|
|
int endIslandIndex = 1;
|
|
|
|
int startIslandIndex;
|
|
|
|
int numElem = getUnionFind().getNumElements();
|
2017-08-01 14:30:58 +02:00
|
|
|
|
2019-01-03 14:26:51 +01:00
|
|
|
// create explicit islands and add bodies to each
|
|
|
|
for (startIslandIndex = 0; startIslandIndex < numElem; startIslandIndex = endIslandIndex)
|
|
|
|
{
|
|
|
|
int islandId = getUnionFind().getElement(startIslandIndex).m_id;
|
2017-08-01 14:30:58 +02:00
|
|
|
|
2019-01-03 14:26:51 +01:00
|
|
|
// find end index
|
|
|
|
for (endIslandIndex = startIslandIndex; (endIslandIndex < numElem) && (getUnionFind().getElement(endIslandIndex).m_id == islandId); endIslandIndex++)
|
|
|
|
{
|
|
|
|
}
|
|
|
|
// check if island is sleeping
|
|
|
|
bool islandSleeping = true;
|
|
|
|
for (int iElem = startIslandIndex; iElem < endIslandIndex; iElem++)
|
|
|
|
{
|
|
|
|
int i = getUnionFind().getElement(iElem).m_sz;
|
|
|
|
btCollisionObject* colObj = collisionObjects[i];
|
|
|
|
if (colObj->isActive())
|
|
|
|
{
|
|
|
|
islandSleeping = false;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
if (!islandSleeping)
|
|
|
|
{
|
|
|
|
// want to count the number of bodies before allocating the island to optimize memory usage of the Island structures
|
|
|
|
int numBodies = endIslandIndex - startIslandIndex;
|
|
|
|
Island* island = allocateIsland(islandId, numBodies);
|
|
|
|
island->isSleeping = false;
|
2017-08-01 14:30:58 +02:00
|
|
|
|
2019-01-03 14:26:51 +01:00
|
|
|
// add bodies to island
|
|
|
|
for (int iElem = startIslandIndex; iElem < endIslandIndex; iElem++)
|
|
|
|
{
|
|
|
|
int i = getUnionFind().getElement(iElem).m_sz;
|
|
|
|
btCollisionObject* colObj = collisionObjects[i];
|
|
|
|
island->bodyArray.push_back(colObj);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
2017-08-01 14:30:58 +02:00
|
|
|
}
|
|
|
|
|
2019-01-03 14:26:51 +01:00
|
|
|
void btSimulationIslandManagerMt::addManifoldsToIslands(btDispatcher* dispatcher)
|
2017-08-01 14:30:58 +02:00
|
|
|
{
|
2019-01-03 14:26:51 +01:00
|
|
|
// walk all the manifolds, activating bodies touched by kinematic objects, and add each manifold to its Island
|
|
|
|
int maxNumManifolds = dispatcher->getNumManifolds();
|
|
|
|
for (int i = 0; i < maxNumManifolds; i++)
|
|
|
|
{
|
|
|
|
btPersistentManifold* manifold = dispatcher->getManifoldByIndexInternal(i);
|
|
|
|
|
|
|
|
const btCollisionObject* colObj0 = static_cast<const btCollisionObject*>(manifold->getBody0());
|
|
|
|
const btCollisionObject* colObj1 = static_cast<const btCollisionObject*>(manifold->getBody1());
|
|
|
|
|
|
|
|
///@todo: check sleeping conditions!
|
|
|
|
if (((colObj0) && colObj0->getActivationState() != ISLAND_SLEEPING) ||
|
|
|
|
((colObj1) && colObj1->getActivationState() != ISLAND_SLEEPING))
|
|
|
|
{
|
|
|
|
//kinematic objects don't merge islands, but wake up all connected objects
|
|
|
|
if (colObj0->isKinematicObject() && colObj0->getActivationState() != ISLAND_SLEEPING)
|
|
|
|
{
|
|
|
|
if (colObj0->hasContactResponse())
|
|
|
|
colObj1->activate();
|
|
|
|
}
|
|
|
|
if (colObj1->isKinematicObject() && colObj1->getActivationState() != ISLAND_SLEEPING)
|
|
|
|
{
|
|
|
|
if (colObj1->hasContactResponse())
|
|
|
|
colObj0->activate();
|
|
|
|
}
|
|
|
|
//filtering for response
|
|
|
|
if (dispatcher->needsResponse(colObj0, colObj1))
|
|
|
|
{
|
|
|
|
// scatter manifolds into various islands
|
|
|
|
int islandId = getIslandId(manifold);
|
|
|
|
// if island not sleeping,
|
|
|
|
if (Island* island = getIsland(islandId))
|
|
|
|
{
|
|
|
|
island->manifoldArray.push_back(manifold);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
2017-08-01 14:30:58 +02:00
|
|
|
}
|
|
|
|
|
2019-01-03 14:26:51 +01:00
|
|
|
void btSimulationIslandManagerMt::addConstraintsToIslands(btAlignedObjectArray<btTypedConstraint*>& constraints)
|
|
|
|
{
|
|
|
|
// walk constraints
|
|
|
|
for (int i = 0; i < constraints.size(); i++)
|
|
|
|
{
|
|
|
|
// scatter constraints into various islands
|
|
|
|
btTypedConstraint* constraint = constraints[i];
|
|
|
|
if (constraint->isEnabled())
|
|
|
|
{
|
2019-06-11 13:18:05 +02:00
|
|
|
int islandId = btGetConstraintIslandId1(constraint);
|
2019-01-03 14:26:51 +01:00
|
|
|
// if island is not sleeping,
|
|
|
|
if (Island* island = getIsland(islandId))
|
|
|
|
{
|
|
|
|
island->constraintArray.push_back(constraint);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
2017-08-01 14:30:58 +02:00
|
|
|
|
|
|
|
void btSimulationIslandManagerMt::mergeIslands()
|
|
|
|
{
|
2019-01-03 14:26:51 +01:00
|
|
|
// sort islands in order of decreasing batch size
|
|
|
|
m_activeIslands.quickSort(IslandBatchSizeSortPredicate());
|
2017-08-01 14:30:58 +02:00
|
|
|
|
2019-01-03 14:26:51 +01:00
|
|
|
// merge small islands to satisfy minimum batch size
|
|
|
|
// find first small batch island
|
|
|
|
int destIslandIndex = m_activeIslands.size();
|
|
|
|
for (int i = 0; i < m_activeIslands.size(); ++i)
|
|
|
|
{
|
|
|
|
Island* island = m_activeIslands[i];
|
|
|
|
int batchSize = calcBatchCost(island);
|
|
|
|
if (batchSize < m_minimumSolverBatchSize)
|
|
|
|
{
|
|
|
|
destIslandIndex = i;
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
int lastIndex = m_activeIslands.size() - 1;
|
|
|
|
while (destIslandIndex < lastIndex)
|
|
|
|
{
|
|
|
|
// merge islands from the back of the list
|
|
|
|
Island* island = m_activeIslands[destIslandIndex];
|
|
|
|
int numBodies = island->bodyArray.size();
|
|
|
|
int numManifolds = island->manifoldArray.size();
|
|
|
|
int numConstraints = island->constraintArray.size();
|
|
|
|
int firstIndex = lastIndex;
|
|
|
|
// figure out how many islands we want to merge and find out how many bodies, manifolds and constraints we will have
|
|
|
|
while (true)
|
|
|
|
{
|
|
|
|
Island* src = m_activeIslands[firstIndex];
|
|
|
|
numBodies += src->bodyArray.size();
|
|
|
|
numManifolds += src->manifoldArray.size();
|
|
|
|
numConstraints += src->constraintArray.size();
|
|
|
|
int batchCost = calcBatchCost(numBodies, numManifolds, numConstraints);
|
|
|
|
if (batchCost >= m_minimumSolverBatchSize)
|
|
|
|
{
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
if (firstIndex - 1 == destIslandIndex)
|
|
|
|
{
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
firstIndex--;
|
|
|
|
}
|
|
|
|
// reserve space for these pointers to minimize reallocation
|
|
|
|
island->bodyArray.reserve(numBodies);
|
|
|
|
island->manifoldArray.reserve(numManifolds);
|
|
|
|
island->constraintArray.reserve(numConstraints);
|
|
|
|
// merge islands
|
|
|
|
for (int i = firstIndex; i <= lastIndex; ++i)
|
|
|
|
{
|
|
|
|
island->append(*m_activeIslands[i]);
|
|
|
|
}
|
|
|
|
// shrink array to exclude the islands that were merged from
|
|
|
|
m_activeIslands.resize(firstIndex);
|
|
|
|
lastIndex = firstIndex - 1;
|
|
|
|
destIslandIndex++;
|
|
|
|
}
|
|
|
|
}
|
2017-08-01 14:30:58 +02:00
|
|
|
|
2018-09-07 16:11:04 +02:00
|
|
|
void btSimulationIslandManagerMt::solveIsland(btConstraintSolver* solver, Island& island, const SolverParams& solverParams)
|
|
|
|
{
|
2019-01-03 14:26:51 +01:00
|
|
|
btPersistentManifold** manifolds = island.manifoldArray.size() ? &island.manifoldArray[0] : NULL;
|
|
|
|
btTypedConstraint** constraintsPtr = island.constraintArray.size() ? &island.constraintArray[0] : NULL;
|
|
|
|
solver->solveGroup(&island.bodyArray[0],
|
|
|
|
island.bodyArray.size(),
|
|
|
|
manifolds,
|
|
|
|
island.manifoldArray.size(),
|
|
|
|
constraintsPtr,
|
|
|
|
island.constraintArray.size(),
|
|
|
|
*solverParams.m_solverInfo,
|
|
|
|
solverParams.m_debugDrawer,
|
|
|
|
solverParams.m_dispatcher);
|
2018-09-07 16:11:04 +02:00
|
|
|
}
|
|
|
|
|
2019-01-03 14:26:51 +01:00
|
|
|
void btSimulationIslandManagerMt::serialIslandDispatch(btAlignedObjectArray<Island*>* islandsPtr, const SolverParams& solverParams)
|
2017-08-01 14:30:58 +02:00
|
|
|
{
|
2019-01-03 14:26:51 +01:00
|
|
|
BT_PROFILE("serialIslandDispatch");
|
|
|
|
// serial dispatch
|
|
|
|
btAlignedObjectArray<Island*>& islands = *islandsPtr;
|
|
|
|
btConstraintSolver* solver = solverParams.m_solverMt ? solverParams.m_solverMt : solverParams.m_solverPool;
|
|
|
|
for (int i = 0; i < islands.size(); ++i)
|
|
|
|
{
|
|
|
|
solveIsland(solver, *islands[i], solverParams);
|
|
|
|
}
|
2017-08-01 14:30:58 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
struct UpdateIslandDispatcher : public btIParallelForBody
|
|
|
|
{
|
2019-01-03 14:26:51 +01:00
|
|
|
btAlignedObjectArray<btSimulationIslandManagerMt::Island*>& m_islandsPtr;
|
|
|
|
const btSimulationIslandManagerMt::SolverParams& m_solverParams;
|
2017-08-01 14:30:58 +02:00
|
|
|
|
2019-01-03 14:26:51 +01:00
|
|
|
UpdateIslandDispatcher(btAlignedObjectArray<btSimulationIslandManagerMt::Island*>& islandsPtr, const btSimulationIslandManagerMt::SolverParams& solverParams)
|
|
|
|
: m_islandsPtr(islandsPtr), m_solverParams(solverParams)
|
|
|
|
{
|
|
|
|
}
|
|
|
|
|
|
|
|
void forLoop(int iBegin, int iEnd) const BT_OVERRIDE
|
|
|
|
{
|
|
|
|
btConstraintSolver* solver = m_solverParams.m_solverPool;
|
|
|
|
for (int i = iBegin; i < iEnd; ++i)
|
|
|
|
{
|
|
|
|
btSimulationIslandManagerMt::Island* island = m_islandsPtr[i];
|
|
|
|
btSimulationIslandManagerMt::solveIsland(solver, *island, m_solverParams);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
};
|
2018-09-07 16:11:04 +02:00
|
|
|
|
2019-01-03 14:26:51 +01:00
|
|
|
void btSimulationIslandManagerMt::parallelIslandDispatch(btAlignedObjectArray<Island*>* islandsPtr, const SolverParams& solverParams)
|
2017-08-01 14:30:58 +02:00
|
|
|
{
|
2019-01-03 14:26:51 +01:00
|
|
|
BT_PROFILE("parallelIslandDispatch");
|
|
|
|
//
|
|
|
|
// if there are islands with many contacts, it may be faster to submit these
|
|
|
|
// large islands *serially* to a single parallel constraint solver, and then later
|
|
|
|
// submit the remaining smaller islands in parallel to multiple sequential solvers.
|
|
|
|
//
|
|
|
|
// Some task schedulers do not deal well with nested parallelFor loops. One implementation
|
|
|
|
// of OpenMP was actually slower than doing everything single-threaded. Intel TBB
|
|
|
|
// on the other hand, seems to do a pretty respectable job with it.
|
|
|
|
//
|
|
|
|
// When solving islands in parallel, the worst case performance happens when there
|
|
|
|
// is one very large island and then perhaps a smattering of very small
|
|
|
|
// islands -- one worker thread takes the large island and the remaining workers
|
|
|
|
// tear through the smaller islands and then sit idle waiting for the first worker
|
|
|
|
// to finish. Solving islands in parallel works best when there are numerous small
|
|
|
|
// islands, roughly equal in size.
|
|
|
|
//
|
|
|
|
// By contrast, the other approach -- the parallel constraint solver -- is only
|
|
|
|
// able to deliver a worthwhile speedup when the island is large. For smaller islands,
|
|
|
|
// it is difficult to extract a useful amount of parallelism -- the overhead of grouping
|
|
|
|
// the constraints into batches and sending the batches to worker threads can nullify
|
|
|
|
// any gains from parallelism.
|
|
|
|
//
|
|
|
|
|
|
|
|
UpdateIslandDispatcher dispatcher(*islandsPtr, solverParams);
|
|
|
|
// We take advantage of the fact the islands are sorted in order of decreasing size
|
|
|
|
int iBegin = 0;
|
|
|
|
if (solverParams.m_solverMt)
|
|
|
|
{
|
|
|
|
while (iBegin < islandsPtr->size())
|
|
|
|
{
|
|
|
|
btSimulationIslandManagerMt::Island* island = (*islandsPtr)[iBegin];
|
|
|
|
if (island->manifoldArray.size() < btSequentialImpulseConstraintSolverMt::s_minimumContactManifoldsForBatching)
|
|
|
|
{
|
|
|
|
// OK to submit the rest of the array in parallel
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
// serial dispatch to parallel solver for large islands (if any)
|
|
|
|
solveIsland(solverParams.m_solverMt, *island, solverParams);
|
|
|
|
++iBegin;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
// parallel dispatch to sequential solvers for rest
|
|
|
|
btParallelFor(iBegin, islandsPtr->size(), 1, dispatcher);
|
2017-08-01 14:30:58 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
///@todo: this is random access, it can be walked 'cache friendly'!
|
2019-01-03 14:26:51 +01:00
|
|
|
void btSimulationIslandManagerMt::buildAndProcessIslands(btDispatcher* dispatcher,
|
|
|
|
btCollisionWorld* collisionWorld,
|
|
|
|
btAlignedObjectArray<btTypedConstraint*>& constraints,
|
|
|
|
const SolverParams& solverParams)
|
2017-08-01 14:30:58 +02:00
|
|
|
{
|
2018-09-07 16:11:04 +02:00
|
|
|
BT_PROFILE("buildAndProcessIslands");
|
2017-08-01 14:30:58 +02:00
|
|
|
btCollisionObjectArray& collisionObjects = collisionWorld->getCollisionObjectArray();
|
|
|
|
|
2019-01-03 14:26:51 +01:00
|
|
|
buildIslands(dispatcher, collisionWorld);
|
2017-08-01 14:30:58 +02:00
|
|
|
|
2019-01-03 14:26:51 +01:00
|
|
|
if (!getSplitIslands())
|
2017-08-01 14:30:58 +02:00
|
|
|
{
|
2019-01-03 14:26:51 +01:00
|
|
|
btPersistentManifold** manifolds = dispatcher->getInternalManifoldPointer();
|
|
|
|
int maxNumManifolds = dispatcher->getNumManifolds();
|
|
|
|
|
|
|
|
for (int i = 0; i < maxNumManifolds; i++)
|
|
|
|
{
|
|
|
|
btPersistentManifold* manifold = manifolds[i];
|
|
|
|
|
|
|
|
const btCollisionObject* colObj0 = static_cast<const btCollisionObject*>(manifold->getBody0());
|
|
|
|
const btCollisionObject* colObj1 = static_cast<const btCollisionObject*>(manifold->getBody1());
|
|
|
|
|
|
|
|
///@todo: check sleeping conditions!
|
|
|
|
if (((colObj0) && colObj0->getActivationState() != ISLAND_SLEEPING) ||
|
|
|
|
((colObj1) && colObj1->getActivationState() != ISLAND_SLEEPING))
|
|
|
|
{
|
|
|
|
//kinematic objects don't merge islands, but wake up all connected objects
|
|
|
|
if (colObj0->isKinematicObject() && colObj0->getActivationState() != ISLAND_SLEEPING)
|
|
|
|
{
|
|
|
|
if (colObj0->hasContactResponse())
|
|
|
|
colObj1->activate();
|
|
|
|
}
|
|
|
|
if (colObj1->isKinematicObject() && colObj1->getActivationState() != ISLAND_SLEEPING)
|
|
|
|
{
|
|
|
|
if (colObj1->hasContactResponse())
|
|
|
|
colObj0->activate();
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
btTypedConstraint** constraintsPtr = constraints.size() ? &constraints[0] : NULL;
|
|
|
|
btConstraintSolver* solver = solverParams.m_solverMt ? solverParams.m_solverMt : solverParams.m_solverPool;
|
|
|
|
solver->solveGroup(&collisionObjects[0],
|
|
|
|
collisionObjects.size(),
|
|
|
|
manifolds,
|
|
|
|
maxNumManifolds,
|
|
|
|
constraintsPtr,
|
|
|
|
constraints.size(),
|
|
|
|
*solverParams.m_solverInfo,
|
|
|
|
solverParams.m_debugDrawer,
|
|
|
|
solverParams.m_dispatcher);
|
2017-08-01 14:30:58 +02:00
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
2019-01-03 14:26:51 +01:00
|
|
|
initIslandPools();
|
|
|
|
|
|
|
|
//traverse the simulation islands, and call the solver, unless all objects are sleeping/deactivated
|
|
|
|
addBodiesToIslands(collisionWorld);
|
|
|
|
addManifoldsToIslands(dispatcher);
|
|
|
|
addConstraintsToIslands(constraints);
|
|
|
|
|
|
|
|
// m_activeIslands array should now contain all non-sleeping Islands, and each Island should
|
|
|
|
// have all the necessary bodies, manifolds and constraints.
|
|
|
|
|
|
|
|
// if we want to merge islands with small batch counts,
|
|
|
|
if (m_minimumSolverBatchSize > 1)
|
|
|
|
{
|
|
|
|
mergeIslands();
|
|
|
|
}
|
|
|
|
// dispatch islands to solver
|
|
|
|
m_islandDispatch(&m_activeIslands, solverParams);
|
2017-08-01 14:30:58 +02:00
|
|
|
}
|
|
|
|
}
|