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 "btBatchedConstraints.h"
18 #include "LinearMath/btIDebugDraw.h"
19 #include "LinearMath/btMinMax.h"
20 #include "LinearMath/btStackAlloc.h"
21 #include "LinearMath/btQuickprof.h"
23 #include <string.h> //for memset
27 const int kNoMerge = -1;
29 bool btBatchedConstraints::s_debugDrawBatches = false;
31 struct btBatchedConstraintInfo
34 int numConstraintRows;
43 btBatchInfo() : numConstraints(0), mergeIndex(kNoMerge) {}
46 bool btBatchedConstraints::validate(btConstraintArray* constraints, const btAlignedObjectArray<btSolverBody>& bodies) const
49 // validate: for debugging only. Verify coloring of bodies, that no body is touched by more than one batch in any given phase
52 const int kUnassignedBatch = -1;
54 btAlignedObjectArray<int> bodyBatchId;
55 for (int iPhase = 0; iPhase < m_phases.size(); ++iPhase)
57 bodyBatchId.resizeNoInitialize(0);
58 bodyBatchId.resize(bodies.size(), kUnassignedBatch);
59 const Range& phase = m_phases[iPhase];
60 for (int iBatch = phase.begin; iBatch < phase.end; ++iBatch)
62 const Range& batch = m_batches[iBatch];
63 for (int iiCons = batch.begin; iiCons < batch.end; ++iiCons)
65 int iCons = m_constraintIndices[iiCons];
66 const btSolverConstraint& cons = constraints->at(iCons);
67 const btSolverBody& bodyA = bodies[cons.m_solverBodyIdA];
68 const btSolverBody& bodyB = bodies[cons.m_solverBodyIdB];
69 if (!bodyA.internalGetInvMass().isZero())
71 int thisBodyBatchId = bodyBatchId[cons.m_solverBodyIdA];
72 if (thisBodyBatchId == kUnassignedBatch)
74 bodyBatchId[cons.m_solverBodyIdA] = iBatch;
76 else if (thisBodyBatchId != iBatch)
78 btAssert(!"dynamic body is used in 2 different batches in the same phase");
82 if (!bodyB.internalGetInvMass().isZero())
84 int thisBodyBatchId = bodyBatchId[cons.m_solverBodyIdB];
85 if (thisBodyBatchId == kUnassignedBatch)
87 bodyBatchId[cons.m_solverBodyIdB] = iBatch;
89 else if (thisBodyBatchId != iBatch)
91 btAssert(!"dynamic body is used in 2 different batches in the same phase");
101 static void debugDrawSingleBatch(const btBatchedConstraints* bc,
102 btConstraintArray* constraints,
103 const btAlignedObjectArray<btSolverBody>& bodies,
105 const btVector3& color,
106 const btVector3& offset)
108 if (bc && bc->m_debugDrawer && iBatch < bc->m_batches.size())
110 const btBatchedConstraints::Range& b = bc->m_batches[iBatch];
111 for (int iiCon = b.begin; iiCon < b.end; ++iiCon)
113 int iCon = bc->m_constraintIndices[iiCon];
114 const btSolverConstraint& con = constraints->at(iCon);
115 int iBody0 = con.m_solverBodyIdA;
116 int iBody1 = con.m_solverBodyIdB;
117 btVector3 pos0 = bodies[iBody0].getWorldTransform().getOrigin() + offset;
118 btVector3 pos1 = bodies[iBody1].getWorldTransform().getOrigin() + offset;
119 bc->m_debugDrawer->drawLine(pos0, pos1, color);
124 static void debugDrawPhase(const btBatchedConstraints* bc,
125 btConstraintArray* constraints,
126 const btAlignedObjectArray<btSolverBody>& bodies,
128 const btVector3& color0,
129 const btVector3& color1,
130 const btVector3& offset)
132 BT_PROFILE("debugDrawPhase");
133 if (bc && bc->m_debugDrawer && iPhase < bc->m_phases.size())
135 const btBatchedConstraints::Range& phase = bc->m_phases[iPhase];
136 for (int iBatch = phase.begin; iBatch < phase.end; ++iBatch)
138 float tt = float(iBatch - phase.begin) / float(btMax(1, phase.end - phase.begin - 1));
139 btVector3 col = lerp(color0, color1, tt);
140 debugDrawSingleBatch(bc, constraints, bodies, iBatch, col, offset);
145 static void debugDrawAllBatches(const btBatchedConstraints* bc,
146 btConstraintArray* constraints,
147 const btAlignedObjectArray<btSolverBody>& bodies)
149 BT_PROFILE("debugDrawAllBatches");
150 if (bc && bc->m_debugDrawer && bc->m_phases.size() > 0)
152 btVector3 bboxMin(BT_LARGE_FLOAT, BT_LARGE_FLOAT, BT_LARGE_FLOAT);
153 btVector3 bboxMax = -bboxMin;
154 for (int iBody = 0; iBody < bodies.size(); ++iBody)
156 const btVector3& pos = bodies[iBody].getWorldTransform().getOrigin();
160 btVector3 bboxExtent = bboxMax - bboxMin;
161 btVector3 offsetBase = btVector3(0, bboxExtent.y() * 1.1f, 0);
162 btVector3 offsetStep = btVector3(0, 0, bboxExtent.z() * 1.1f);
163 int numPhases = bc->m_phases.size();
164 for (int iPhase = 0; iPhase < numPhases; ++iPhase)
166 float b = float(iPhase) / float(numPhases - 1);
167 btVector3 color0 = btVector3(1, 0, b);
168 btVector3 color1 = btVector3(0, 1, b);
169 btVector3 offset = offsetBase + offsetStep * (float(iPhase) - float(numPhases - 1) * 0.5);
170 debugDrawPhase(bc, constraints, bodies, iPhase, color0, color1, offset);
175 static void initBatchedBodyDynamicFlags(btAlignedObjectArray<bool>* outBodyDynamicFlags, const btAlignedObjectArray<btSolverBody>& bodies)
177 BT_PROFILE("initBatchedBodyDynamicFlags");
178 btAlignedObjectArray<bool>& bodyDynamicFlags = *outBodyDynamicFlags;
179 bodyDynamicFlags.resizeNoInitialize(bodies.size());
180 for (int i = 0; i < bodies.size(); ++i)
182 const btSolverBody& body = bodies[i];
183 bodyDynamicFlags[i] = (body.internalGetInvMass().x() > btScalar(0));
187 static int runLengthEncodeConstraintInfo(btBatchedConstraintInfo* outConInfos, int numConstraints)
189 BT_PROFILE("runLengthEncodeConstraintInfo");
190 // detect and run-length encode constraint rows that repeat the same bodies
193 while (iSrc < numConstraints)
195 const btBatchedConstraintInfo& srcConInfo = outConInfos[iSrc];
196 btBatchedConstraintInfo& conInfo = outConInfos[iDest];
197 conInfo.constraintIndex = iSrc;
198 conInfo.bodyIds[0] = srcConInfo.bodyIds[0];
199 conInfo.bodyIds[1] = srcConInfo.bodyIds[1];
200 while (iSrc < numConstraints && outConInfos[iSrc].bodyIds[0] == srcConInfo.bodyIds[0] && outConInfos[iSrc].bodyIds[1] == srcConInfo.bodyIds[1])
204 conInfo.numConstraintRows = iSrc - conInfo.constraintIndex;
210 struct ReadSolverConstraintsLoop : public btIParallelForBody
212 btBatchedConstraintInfo* m_outConInfos;
213 btConstraintArray* m_constraints;
215 ReadSolverConstraintsLoop(btBatchedConstraintInfo* outConInfos, btConstraintArray* constraints)
217 m_outConInfos = outConInfos;
218 m_constraints = constraints;
220 void forLoop(int iBegin, int iEnd) const BT_OVERRIDE
222 for (int i = iBegin; i < iEnd; ++i)
224 btBatchedConstraintInfo& conInfo = m_outConInfos[i];
225 const btSolverConstraint& con = m_constraints->at(i);
226 conInfo.bodyIds[0] = con.m_solverBodyIdA;
227 conInfo.bodyIds[1] = con.m_solverBodyIdB;
228 conInfo.constraintIndex = i;
229 conInfo.numConstraintRows = 1;
234 static int initBatchedConstraintInfo(btBatchedConstraintInfo* outConInfos, btConstraintArray* constraints)
236 BT_PROFILE("initBatchedConstraintInfo");
237 int numConstraints = constraints->size();
238 bool inParallel = true;
241 ReadSolverConstraintsLoop loop(outConInfos, constraints);
242 int grainSize = 1200;
243 btParallelFor(0, numConstraints, grainSize, loop);
247 for (int i = 0; i < numConstraints; ++i)
249 btBatchedConstraintInfo& conInfo = outConInfos[i];
250 const btSolverConstraint& con = constraints->at(i);
251 conInfo.bodyIds[0] = con.m_solverBodyIdA;
252 conInfo.bodyIds[1] = con.m_solverBodyIdB;
253 conInfo.constraintIndex = i;
254 conInfo.numConstraintRows = 1;
257 bool useRunLengthEncoding = true;
258 if (useRunLengthEncoding)
260 numConstraints = runLengthEncodeConstraintInfo(outConInfos, numConstraints);
262 return numConstraints;
265 static void expandConstraintRowsInPlace(int* constraintBatchIds, const btBatchedConstraintInfo* conInfos, int numConstraints, int numConstraintRows)
267 BT_PROFILE("expandConstraintRowsInPlace");
268 if (numConstraintRows > numConstraints)
270 // we walk the array in reverse to avoid overwriteing
271 for (int iCon = numConstraints - 1; iCon >= 0; --iCon)
273 const btBatchedConstraintInfo& conInfo = conInfos[iCon];
274 int iBatch = constraintBatchIds[iCon];
275 for (int i = conInfo.numConstraintRows - 1; i >= 0; --i)
277 int iDest = conInfo.constraintIndex + i;
278 btAssert(iDest >= iCon);
279 btAssert(iDest >= 0 && iDest < numConstraintRows);
280 constraintBatchIds[iDest] = iBatch;
286 static void expandConstraintRows(int* destConstraintBatchIds, const int* srcConstraintBatchIds, const btBatchedConstraintInfo* conInfos, int numConstraints, int numConstraintRows)
288 BT_PROFILE("expandConstraintRows");
289 for (int iCon = 0; iCon < numConstraints; ++iCon)
291 const btBatchedConstraintInfo& conInfo = conInfos[iCon];
292 int iBatch = srcConstraintBatchIds[iCon];
293 for (int i = 0; i < conInfo.numConstraintRows; ++i)
295 int iDest = conInfo.constraintIndex + i;
296 btAssert(iDest >= iCon);
297 btAssert(iDest >= 0 && iDest < numConstraintRows);
298 destConstraintBatchIds[iDest] = iBatch;
303 struct ExpandConstraintRowsLoop : public btIParallelForBody
305 int* m_destConstraintBatchIds;
306 const int* m_srcConstraintBatchIds;
307 const btBatchedConstraintInfo* m_conInfos;
308 int m_numConstraintRows;
310 ExpandConstraintRowsLoop(int* destConstraintBatchIds, const int* srcConstraintBatchIds, const btBatchedConstraintInfo* conInfos, int numConstraintRows)
312 m_destConstraintBatchIds = destConstraintBatchIds;
313 m_srcConstraintBatchIds = srcConstraintBatchIds;
314 m_conInfos = conInfos;
315 m_numConstraintRows = numConstraintRows;
317 void forLoop(int iBegin, int iEnd) const BT_OVERRIDE
319 expandConstraintRows(m_destConstraintBatchIds, m_srcConstraintBatchIds + iBegin, m_conInfos + iBegin, iEnd - iBegin, m_numConstraintRows);
323 static void expandConstraintRowsMt(int* destConstraintBatchIds, const int* srcConstraintBatchIds, const btBatchedConstraintInfo* conInfos, int numConstraints, int numConstraintRows)
325 BT_PROFILE("expandConstraintRowsMt");
326 ExpandConstraintRowsLoop loop(destConstraintBatchIds, srcConstraintBatchIds, conInfos, numConstraintRows);
328 btParallelFor(0, numConstraints, grainSize, loop);
331 static void initBatchedConstraintInfoArray(btAlignedObjectArray<btBatchedConstraintInfo>* outConInfos, btConstraintArray* constraints)
333 BT_PROFILE("initBatchedConstraintInfoArray");
334 btAlignedObjectArray<btBatchedConstraintInfo>& conInfos = *outConInfos;
335 int numConstraints = constraints->size();
336 conInfos.resizeNoInitialize(numConstraints);
338 int newSize = initBatchedConstraintInfo(&outConInfos->at(0), constraints);
339 conInfos.resizeNoInitialize(newSize);
342 static void mergeSmallBatches(btBatchInfo* batches, int iBeginBatch, int iEndBatch, int minBatchSize, int maxBatchSize)
344 BT_PROFILE("mergeSmallBatches");
345 for (int iBatch = iEndBatch - 1; iBatch >= iBeginBatch; --iBatch)
347 btBatchInfo& batch = batches[iBatch];
348 if (batch.mergeIndex == kNoMerge && batch.numConstraints > 0 && batch.numConstraints < minBatchSize)
350 for (int iDestBatch = iBatch - 1; iDestBatch >= iBeginBatch; --iDestBatch)
352 btBatchInfo& destBatch = batches[iDestBatch];
353 if (destBatch.mergeIndex == kNoMerge && (destBatch.numConstraints + batch.numConstraints) < maxBatchSize)
355 destBatch.numConstraints += batch.numConstraints;
356 batch.numConstraints = 0;
357 batch.mergeIndex = iDestBatch;
363 // flatten mergeIndexes
364 // e.g. in case where A was merged into B and then B was merged into C, we need A to point to C instead of B
365 // Note: loop goes forward through batches because batches always merge from higher indexes to lower,
366 // so by going from low to high it reduces the amount of trail-following
367 for (int iBatch = iBeginBatch; iBatch < iEndBatch; ++iBatch)
369 btBatchInfo& batch = batches[iBatch];
370 if (batch.mergeIndex != kNoMerge)
372 int iMergeDest = batches[batch.mergeIndex].mergeIndex;
373 // follow trail of merges to the end
374 while (iMergeDest != kNoMerge)
376 int iNext = batches[iMergeDest].mergeIndex;
377 if (iNext == kNoMerge)
379 batch.mergeIndex = iMergeDest;
388 static void updateConstraintBatchIdsForMerges(int* constraintBatchIds, int numConstraints, const btBatchInfo* batches, int numBatches)
390 BT_PROFILE("updateConstraintBatchIdsForMerges");
391 // update batchIds to account for merges
392 for (int i = 0; i < numConstraints; ++i)
394 int iBatch = constraintBatchIds[i];
395 btAssert(iBatch < numBatches);
396 // if this constraint references a batch that was merged into another batch
397 if (batches[iBatch].mergeIndex != kNoMerge)
400 constraintBatchIds[i] = batches[iBatch].mergeIndex;
405 struct UpdateConstraintBatchIdsForMergesLoop : public btIParallelForBody
407 int* m_constraintBatchIds;
408 const btBatchInfo* m_batches;
411 UpdateConstraintBatchIdsForMergesLoop(int* constraintBatchIds, const btBatchInfo* batches, int numBatches)
413 m_constraintBatchIds = constraintBatchIds;
415 m_numBatches = numBatches;
417 void forLoop(int iBegin, int iEnd) const BT_OVERRIDE
419 BT_PROFILE("UpdateConstraintBatchIdsForMergesLoop");
420 updateConstraintBatchIdsForMerges(m_constraintBatchIds + iBegin, iEnd - iBegin, m_batches, m_numBatches);
424 static void updateConstraintBatchIdsForMergesMt(int* constraintBatchIds, int numConstraints, const btBatchInfo* batches, int numBatches)
426 BT_PROFILE("updateConstraintBatchIdsForMergesMt");
427 UpdateConstraintBatchIdsForMergesLoop loop(constraintBatchIds, batches, numBatches);
429 btParallelFor(0, numConstraints, grainSize, loop);
432 inline bool BatchCompare(const btBatchedConstraints::Range& a, const btBatchedConstraints::Range& b)
434 int lenA = a.end - a.begin;
435 int lenB = b.end - b.begin;
439 static void writeOutConstraintIndicesForRangeOfBatches(btBatchedConstraints* bc,
440 const int* constraintBatchIds,
442 int* constraintIdPerBatch,
446 BT_PROFILE("writeOutConstraintIndicesForRangeOfBatches");
447 for (int iCon = 0; iCon < numConstraints; ++iCon)
449 int iBatch = constraintBatchIds[iCon];
450 if (iBatch >= batchBegin && iBatch < batchEnd)
452 int iDestCon = constraintIdPerBatch[iBatch];
453 constraintIdPerBatch[iBatch] = iDestCon + 1;
454 bc->m_constraintIndices[iDestCon] = iCon;
459 struct WriteOutConstraintIndicesLoop : public btIParallelForBody
461 btBatchedConstraints* m_batchedConstraints;
462 const int* m_constraintBatchIds;
463 int m_numConstraints;
464 int* m_constraintIdPerBatch;
465 int m_maxNumBatchesPerPhase;
467 WriteOutConstraintIndicesLoop(btBatchedConstraints* bc, const int* constraintBatchIds, int numConstraints, int* constraintIdPerBatch, int maxNumBatchesPerPhase)
469 m_batchedConstraints = bc;
470 m_constraintBatchIds = constraintBatchIds;
471 m_numConstraints = numConstraints;
472 m_constraintIdPerBatch = constraintIdPerBatch;
473 m_maxNumBatchesPerPhase = maxNumBatchesPerPhase;
475 void forLoop(int iBegin, int iEnd) const BT_OVERRIDE
477 BT_PROFILE("WriteOutConstraintIndicesLoop");
478 int batchBegin = iBegin * m_maxNumBatchesPerPhase;
479 int batchEnd = iEnd * m_maxNumBatchesPerPhase;
480 writeOutConstraintIndicesForRangeOfBatches(m_batchedConstraints,
481 m_constraintBatchIds,
483 m_constraintIdPerBatch,
489 static void writeOutConstraintIndicesMt(btBatchedConstraints* bc,
490 const int* constraintBatchIds,
492 int* constraintIdPerBatch,
493 int maxNumBatchesPerPhase,
496 BT_PROFILE("writeOutConstraintIndicesMt");
497 bool inParallel = true;
500 WriteOutConstraintIndicesLoop loop(bc, constraintBatchIds, numConstraints, constraintIdPerBatch, maxNumBatchesPerPhase);
501 btParallelFor(0, numPhases, 1, loop);
505 for (int iCon = 0; iCon < numConstraints; ++iCon)
507 int iBatch = constraintBatchIds[iCon];
508 int iDestCon = constraintIdPerBatch[iBatch];
509 constraintIdPerBatch[iBatch] = iDestCon + 1;
510 bc->m_constraintIndices[iDestCon] = iCon;
515 static void writeGrainSizes(btBatchedConstraints* bc)
517 typedef btBatchedConstraints::Range Range;
518 int numPhases = bc->m_phases.size();
519 bc->m_phaseGrainSize.resizeNoInitialize(numPhases);
520 int numThreads = btGetTaskScheduler()->getNumThreads();
521 for (int iPhase = 0; iPhase < numPhases; ++iPhase)
523 const Range& phase = bc->m_phases[iPhase];
524 int numBatches = phase.end - phase.begin;
525 float grainSize = std::floor((0.25f * numBatches / float(numThreads)) + 0.0f);
526 bc->m_phaseGrainSize[iPhase] = btMax(1, int(grainSize));
530 static void writeOutBatches(btBatchedConstraints* bc,
531 const int* constraintBatchIds,
533 const btBatchInfo* batches,
535 int maxNumBatchesPerPhase,
538 BT_PROFILE("writeOutBatches");
539 typedef btBatchedConstraints::Range Range;
540 bc->m_constraintIndices.reserve(numConstraints);
541 bc->m_batches.resizeNoInitialize(0);
542 bc->m_phases.resizeNoInitialize(0);
544 //int maxNumBatches = numPhases * maxNumBatchesPerPhase;
546 int* constraintIdPerBatch = batchWork; // for each batch, keep an index into the next available slot in the m_constraintIndices array
548 for (int iPhase = 0; iPhase < numPhases; ++iPhase)
550 int curPhaseBegin = bc->m_batches.size();
551 int iBegin = iPhase * maxNumBatchesPerPhase;
552 int iEnd = iBegin + maxNumBatchesPerPhase;
553 for (int i = iBegin; i < iEnd; ++i)
555 const btBatchInfo& batch = batches[i];
556 int curBatchBegin = iConstraint;
557 constraintIdPerBatch[i] = curBatchBegin; // record the start of each batch in m_constraintIndices array
558 int numConstraints = batch.numConstraints;
559 iConstraint += numConstraints;
560 if (numConstraints > 0)
562 bc->m_batches.push_back(Range(curBatchBegin, iConstraint));
565 // if any batches were emitted this phase,
566 if (bc->m_batches.size() > curPhaseBegin)
569 bc->m_phases.push_back(Range(curPhaseBegin, bc->m_batches.size()));
573 btAssert(iConstraint == numConstraints);
574 bc->m_constraintIndices.resizeNoInitialize(numConstraints);
575 writeOutConstraintIndicesMt(bc, constraintBatchIds, numConstraints, constraintIdPerBatch, maxNumBatchesPerPhase, numPhases);
578 for (int iPhase = 0; iPhase < bc->m_phases.size(); ++iPhase)
580 // sort the batches from largest to smallest (can be helpful to some task schedulers)
581 const Range& curBatches = bc->m_phases[iPhase];
582 bc->m_batches.quickSortInternal(BatchCompare, curBatches.begin, curBatches.end - 1);
584 bc->m_phaseOrder.resize(bc->m_phases.size());
585 for (int i = 0; i < bc->m_phases.size(); ++i)
587 bc->m_phaseOrder[i] = i;
593 // PreallocatedMemoryHelper -- helper object for allocating a number of chunks of memory in a single contiguous block.
594 // It is generally more efficient to do a single larger allocation than many smaller allocations.
598 // btVector3* bodyPositions = NULL;
599 // btBatchedConstraintInfo* conInfos = NULL;
601 // PreallocatedMemoryHelper<8> memHelper;
602 // memHelper.addChunk( (void**) &bodyPositions, sizeof( btVector3 ) * bodies.size() );
603 // memHelper.addChunk( (void**) &conInfos, sizeof( btBatchedConstraintInfo ) * numConstraints );
604 // void* memPtr = malloc( memHelper.getSizeToAllocate() ); // allocate the memory
605 // memHelper.setChunkPointers( memPtr ); // update pointers to chunks
608 class PreallocatedMemoryHelper
619 PreallocatedMemoryHelper() { m_numChunks = 0; }
620 void addChunk(void** ptr, size_t sz)
622 btAssert(m_numChunks < N);
625 Chunk& chunk = m_chunks[m_numChunks];
631 size_t getSizeToAllocate() const
633 size_t totalSize = 0;
634 for (int i = 0; i < m_numChunks; ++i)
636 totalSize += m_chunks[i].size;
640 void setChunkPointers(void* mem) const
642 size_t totalSize = 0;
643 for (int i = 0; i < m_numChunks; ++i)
645 const Chunk& chunk = m_chunks[i];
646 char* chunkPtr = static_cast<char*>(mem) + totalSize;
647 *chunk.ptr = chunkPtr;
648 totalSize += chunk.size;
653 static btVector3 findMaxDynamicConstraintExtent(
654 btVector3* bodyPositions,
655 bool* bodyDynamicFlags,
656 btBatchedConstraintInfo* conInfos,
660 BT_PROFILE("findMaxDynamicConstraintExtent");
661 btVector3 consExtent = btVector3(1, 1, 1) * 0.001;
662 for (int iCon = 0; iCon < numConstraints; ++iCon)
664 const btBatchedConstraintInfo& con = conInfos[iCon];
665 int iBody0 = con.bodyIds[0];
666 int iBody1 = con.bodyIds[1];
667 btAssert(iBody0 >= 0 && iBody0 < numBodies);
668 btAssert(iBody1 >= 0 && iBody1 < numBodies);
669 // is it a dynamic constraint?
670 if (bodyDynamicFlags[iBody0] && bodyDynamicFlags[iBody1])
672 btVector3 delta = bodyPositions[iBody1] - bodyPositions[iBody0];
673 consExtent.setMax(delta.absolute());
683 SIMD_FORCE_INLINE const int& operator[](int i) const { return m_ints[i]; }
684 SIMD_FORCE_INLINE int& operator[](int i) { return m_ints[i]; }
687 struct AssignConstraintsToGridBatchesParams
689 bool* bodyDynamicFlags;
690 btIntVec3* bodyGridCoords;
692 btBatchedConstraintInfo* conInfos;
693 int* constraintBatchIds;
694 btIntVec3 gridChunkDim;
695 int maxNumBatchesPerPhase;
699 AssignConstraintsToGridBatchesParams()
701 memset(this, 0, sizeof(*this));
705 static void assignConstraintsToGridBatches(const AssignConstraintsToGridBatchesParams& params, int iConBegin, int iConEnd)
707 BT_PROFILE("assignConstraintsToGridBatches");
708 // (can be done in parallel)
709 for (int iCon = iConBegin; iCon < iConEnd; ++iCon)
711 const btBatchedConstraintInfo& con = params.conInfos[iCon];
712 int iBody0 = con.bodyIds[0];
713 int iBody1 = con.bodyIds[1];
714 int iPhase = iCon; //iBody0; // pseudorandom choice to distribute evenly amongst phases
715 iPhase &= params.phaseMask;
717 // is it a dynamic constraint?
718 if (params.bodyDynamicFlags[iBody0] && params.bodyDynamicFlags[iBody1])
720 const btIntVec3& body0Coords = params.bodyGridCoords[iBody0];
721 const btIntVec3& body1Coords = params.bodyGridCoords[iBody1];
722 // for each dimension x,y,z,
723 for (int i = 0; i < 3; ++i)
725 int coordMin = btMin(body0Coords.m_ints[i], body1Coords.m_ints[i]);
726 int coordMax = btMax(body0Coords.m_ints[i], body1Coords.m_ints[i]);
727 if (coordMin != coordMax)
729 btAssert(coordMax == coordMin + 1);
730 if ((coordMin & 1) == 0)
732 iPhase &= ~(1 << i); // force bit off
736 iPhase |= (1 << i); // force bit on
737 iPhase &= params.phaseMask;
740 gridCoord[i] = coordMin;
745 if (!params.bodyDynamicFlags[iBody0])
747 iBody0 = con.bodyIds[1];
749 btAssert(params.bodyDynamicFlags[iBody0]);
750 const btIntVec3& body0Coords = params.bodyGridCoords[iBody0];
751 // for each dimension x,y,z,
752 for (int i = 0; i < 3; ++i)
754 gridCoord[i] = body0Coords.m_ints[i];
757 // calculate chunk coordinates
759 btIntVec3 gridChunkDim = params.gridChunkDim;
760 // for each dimension x,y,z,
761 for (int i = 0; i < 3; ++i)
763 int coordOffset = (iPhase >> i) & 1;
764 chunkCoord[i] = (gridCoord[i] - coordOffset) / 2;
765 btClamp(chunkCoord[i], 0, gridChunkDim[i] - 1);
766 btAssert(chunkCoord[i] < gridChunkDim[i]);
768 int iBatch = iPhase * params.maxNumBatchesPerPhase + chunkCoord[0] + chunkCoord[1] * gridChunkDim[0] + chunkCoord[2] * gridChunkDim[0] * gridChunkDim[1];
769 btAssert(iBatch >= 0 && iBatch < params.maxNumBatchesPerPhase * params.numPhases);
770 params.constraintBatchIds[iCon] = iBatch;
774 struct AssignConstraintsToGridBatchesLoop : public btIParallelForBody
776 const AssignConstraintsToGridBatchesParams* m_params;
778 AssignConstraintsToGridBatchesLoop(const AssignConstraintsToGridBatchesParams& params)
782 void forLoop(int iBegin, int iEnd) const BT_OVERRIDE
784 assignConstraintsToGridBatches(*m_params, iBegin, iEnd);
789 // setupSpatialGridBatchesMt -- generate batches using a uniform 3D grid
793 Bodies are treated as 3D points at their center of mass. We only consider dynamic bodies at this stage,
794 because only dynamic bodies are mutated when a constraint is solved, thus subject to race conditions.
796 1. Compute a bounding box around all dynamic bodies
797 2. Compute the maximum extent of all dynamic constraints. Each dynamic constraint is treated as a line segment, and we need the size of
798 box that will fully enclose any single dynamic constraint
800 3. Establish the cell size of our grid, the cell size in each dimension must be at least as large as the dynamic constraints max-extent,
801 so that no dynamic constraint can span more than 2 cells of our grid on any axis of the grid. The cell size should be adjusted
802 larger in order to keep the total number of cells from being excessively high
804 Key idea: Given that each constraint spans 1 or 2 grid cells in each dimension, we can handle all constraints by processing
805 in chunks of 2x2x2 cells with 8 different 1-cell offsets ((0,0,0),(0,0,1),(0,1,0),(0,1,1),(1,0,0)...).
806 For each of the 8 offsets, we create a phase, and for each 2x2x2 chunk with dynamic constraints becomes a batch in that phase.
808 4. Once the grid is established, we can calculate for each constraint which phase and batch it belongs in.
810 5. Do a merge small batches on the batches of each phase separately, to try to even out the sizes of batches
812 Optionally, we can "collapse" one dimension of our 3D grid to turn it into a 2D grid, which reduces the number of phases
813 to 4. With fewer phases, there are more constraints per phase and this makes it easier to create batches of a useful size.
816 static void setupSpatialGridBatchesMt(
817 btBatchedConstraints* batchedConstraints,
818 btAlignedObjectArray<char>* scratchMemory,
819 btConstraintArray* constraints,
820 const btAlignedObjectArray<btSolverBody>& bodies,
825 BT_PROFILE("setupSpatialGridBatchesMt");
826 const int numPhases = 8;
827 int numConstraints = constraints->size();
828 int numConstraintRows = constraints->size();
830 const int maxGridChunkCount = 128;
831 int allocNumBatchesPerPhase = maxGridChunkCount;
832 int minNumBatchesPerPhase = 16;
833 int allocNumBatches = allocNumBatchesPerPhase * numPhases;
835 btVector3* bodyPositions = NULL;
836 bool* bodyDynamicFlags = NULL;
837 btIntVec3* bodyGridCoords = NULL;
838 btBatchInfo* batches = NULL;
839 int* batchWork = NULL;
840 btBatchedConstraintInfo* conInfos = NULL;
841 int* constraintBatchIds = NULL;
842 int* constraintRowBatchIds = NULL;
844 PreallocatedMemoryHelper<10> memHelper;
845 memHelper.addChunk((void**)&bodyPositions, sizeof(btVector3) * bodies.size());
846 memHelper.addChunk((void**)&bodyDynamicFlags, sizeof(bool) * bodies.size());
847 memHelper.addChunk((void**)&bodyGridCoords, sizeof(btIntVec3) * bodies.size());
848 memHelper.addChunk((void**)&batches, sizeof(btBatchInfo) * allocNumBatches);
849 memHelper.addChunk((void**)&batchWork, sizeof(int) * allocNumBatches);
850 memHelper.addChunk((void**)&conInfos, sizeof(btBatchedConstraintInfo) * numConstraints);
851 memHelper.addChunk((void**)&constraintBatchIds, sizeof(int) * numConstraints);
852 memHelper.addChunk((void**)&constraintRowBatchIds, sizeof(int) * numConstraintRows);
853 size_t scratchSize = memHelper.getSizeToAllocate();
854 // if we need to reallocate
855 if (static_cast<size_t>(scratchMemory->capacity()) < scratchSize)
857 // allocate 6.25% extra to avoid repeated reallocs
858 scratchMemory->reserve(scratchSize + scratchSize / 16);
860 scratchMemory->resizeNoInitialize(scratchSize);
861 char* memPtr = &scratchMemory->at(0);
862 memHelper.setChunkPointers(memPtr);
865 numConstraints = initBatchedConstraintInfo(conInfos, constraints);
867 // compute bounding box around all dynamic bodies
868 // (could be done in parallel)
869 btVector3 bboxMin(BT_LARGE_FLOAT, BT_LARGE_FLOAT, BT_LARGE_FLOAT);
870 btVector3 bboxMax = -bboxMin;
871 //int dynamicBodyCount = 0;
872 for (int i = 0; i < bodies.size(); ++i)
874 const btSolverBody& body = bodies[i];
875 btVector3 bodyPos = body.getWorldTransform().getOrigin();
876 bool isDynamic = (body.internalGetInvMass().x() > btScalar(0));
877 bodyPositions[i] = bodyPos;
878 bodyDynamicFlags[i] = isDynamic;
881 //dynamicBodyCount++;
882 bboxMin.setMin(bodyPos);
883 bboxMax.setMax(bodyPos);
887 // find max extent of all dynamic constraints
888 // (could be done in parallel)
889 btVector3 consExtent = findMaxDynamicConstraintExtent(bodyPositions, bodyDynamicFlags, conInfos, numConstraints, bodies.size());
891 btVector3 gridExtent = bboxMax - bboxMin;
893 gridExtent.setMax(btVector3(btScalar(1), btScalar(1), btScalar(1)));
895 btVector3 gridCellSize = consExtent;
897 gridDim[0] = int(1.0 + gridExtent.x() / gridCellSize.x());
898 gridDim[1] = int(1.0 + gridExtent.y() / gridCellSize.y());
899 gridDim[2] = int(1.0 + gridExtent.z() / gridCellSize.z());
901 // if we can collapse an axis, it will cut our number of phases in half which could be more efficient
903 bool collapseAxis = use2DGrid;
906 // pick the smallest axis to collapse, leaving us with the greatest number of cells in our grid
907 int iAxisToCollapse = 0;
908 int axisDim = gridDim[iAxisToCollapse];
910 for (int i = 0; i < 3; ++i)
912 if (gridDim[i] < axisDim)
915 axisDim = gridDim[i];
919 gridCellSize[iAxisToCollapse] = gridExtent[iAxisToCollapse] * 2.0f;
920 phaseMask &= ~(1 << iAxisToCollapse);
923 int numGridChunks = 0;
924 btIntVec3 gridChunkDim; // each chunk is 2x2x2 group of cells
927 gridDim[0] = int(1.0 + gridExtent.x() / gridCellSize.x());
928 gridDim[1] = int(1.0 + gridExtent.y() / gridCellSize.y());
929 gridDim[2] = int(1.0 + gridExtent.z() / gridCellSize.z());
930 gridChunkDim[0] = btMax(1, (gridDim[0] + 0) / 2);
931 gridChunkDim[1] = btMax(1, (gridDim[1] + 0) / 2);
932 gridChunkDim[2] = btMax(1, (gridDim[2] + 0) / 2);
933 numGridChunks = gridChunkDim[0] * gridChunkDim[1] * gridChunkDim[2];
934 float nChunks = float(gridChunkDim[0]) * float(gridChunkDim[1]) * float(gridChunkDim[2]); // suceptible to integer overflow
935 if (numGridChunks <= maxGridChunkCount && nChunks <= maxGridChunkCount)
939 gridCellSize *= 1.25; // should roughly cut numCells in half
941 btAssert(numGridChunks <= maxGridChunkCount);
942 int maxNumBatchesPerPhase = numGridChunks;
944 // for each dynamic body, compute grid coords
945 btVector3 invGridCellSize = btVector3(1, 1, 1) / gridCellSize;
946 // (can be done in parallel)
947 for (int iBody = 0; iBody < bodies.size(); ++iBody)
949 btIntVec3& coords = bodyGridCoords[iBody];
950 if (bodyDynamicFlags[iBody])
952 btVector3 v = (bodyPositions[iBody] - bboxMin) * invGridCellSize;
953 coords.m_ints[0] = int(v.x());
954 coords.m_ints[1] = int(v.y());
955 coords.m_ints[2] = int(v.z());
956 btAssert(coords.m_ints[0] >= 0 && coords.m_ints[0] < gridDim[0]);
957 btAssert(coords.m_ints[1] >= 0 && coords.m_ints[1] < gridDim[1]);
958 btAssert(coords.m_ints[2] >= 0 && coords.m_ints[2] < gridDim[2]);
962 coords.m_ints[0] = -1;
963 coords.m_ints[1] = -1;
964 coords.m_ints[2] = -1;
968 for (int iPhase = 0; iPhase < numPhases; ++iPhase)
970 int batchBegin = iPhase * maxNumBatchesPerPhase;
971 int batchEnd = batchBegin + maxNumBatchesPerPhase;
972 for (int iBatch = batchBegin; iBatch < batchEnd; ++iBatch)
974 btBatchInfo& batch = batches[iBatch];
975 batch = btBatchInfo();
980 AssignConstraintsToGridBatchesParams params;
981 params.bodyDynamicFlags = bodyDynamicFlags;
982 params.bodyGridCoords = bodyGridCoords;
983 params.numBodies = bodies.size();
984 params.conInfos = conInfos;
985 params.constraintBatchIds = constraintBatchIds;
986 params.gridChunkDim = gridChunkDim;
987 params.maxNumBatchesPerPhase = maxNumBatchesPerPhase;
988 params.numPhases = numPhases;
989 params.phaseMask = phaseMask;
990 bool inParallel = true;
993 AssignConstraintsToGridBatchesLoop loop(params);
995 btParallelFor(0, numConstraints, grainSize, loop);
999 assignConstraintsToGridBatches(params, 0, numConstraints);
1002 for (int iCon = 0; iCon < numConstraints; ++iCon)
1004 const btBatchedConstraintInfo& con = conInfos[iCon];
1005 int iBatch = constraintBatchIds[iCon];
1006 btBatchInfo& batch = batches[iBatch];
1007 batch.numConstraints += con.numConstraintRows;
1010 for (int iPhase = 0; iPhase < numPhases; ++iPhase)
1012 // if phase is legit,
1013 if (iPhase == (iPhase & phaseMask))
1015 int iBeginBatch = iPhase * maxNumBatchesPerPhase;
1016 int iEndBatch = iBeginBatch + maxNumBatchesPerPhase;
1017 mergeSmallBatches(batches, iBeginBatch, iEndBatch, minBatchSize, maxBatchSize);
1020 // all constraints have been assigned a batchId
1021 updateConstraintBatchIdsForMergesMt(constraintBatchIds, numConstraints, batches, maxNumBatchesPerPhase * numPhases);
1023 if (numConstraintRows > numConstraints)
1025 expandConstraintRowsMt(&constraintRowBatchIds[0], &constraintBatchIds[0], &conInfos[0], numConstraints, numConstraintRows);
1029 constraintRowBatchIds = constraintBatchIds;
1032 writeOutBatches(batchedConstraints, constraintRowBatchIds, numConstraintRows, batches, batchWork, maxNumBatchesPerPhase, numPhases);
1033 btAssert(batchedConstraints->validate(constraints, bodies));
1036 static void setupSingleBatch(
1037 btBatchedConstraints* bc,
1040 BT_PROFILE("setupSingleBatch");
1041 typedef btBatchedConstraints::Range Range;
1043 bc->m_constraintIndices.resize(numConstraints);
1044 for (int i = 0; i < numConstraints; ++i)
1046 bc->m_constraintIndices[i] = i;
1049 bc->m_batches.resizeNoInitialize(0);
1050 bc->m_phases.resizeNoInitialize(0);
1051 bc->m_phaseOrder.resizeNoInitialize(0);
1052 bc->m_phaseGrainSize.resizeNoInitialize(0);
1054 if (numConstraints > 0)
1056 bc->m_batches.push_back(Range(0, numConstraints));
1057 bc->m_phases.push_back(Range(0, 1));
1058 bc->m_phaseOrder.push_back(0);
1059 bc->m_phaseGrainSize.push_back(1);
1063 void btBatchedConstraints::setup(
1064 btConstraintArray* constraints,
1065 const btAlignedObjectArray<btSolverBody>& bodies,
1066 BatchingMethod batchingMethod,
1069 btAlignedObjectArray<char>* scratchMemory)
1071 if (constraints->size() >= minBatchSize * 4)
1073 bool use2DGrid = batchingMethod == BATCHING_METHOD_SPATIAL_GRID_2D;
1074 setupSpatialGridBatchesMt(this, scratchMemory, constraints, bodies, minBatchSize, maxBatchSize, use2DGrid);
1075 if (s_debugDrawBatches)
1077 debugDrawAllBatches(this, constraints, bodies);
1082 setupSingleBatch(this, constraints->size());