Planet
navi homePPSaboutscreenshotsdownloaddevelopmentforum

source: code/branches/physics/src/bullet/BulletMultiThreaded/SpuParallelSolver.cpp @ 1966

Last change on this file since 1966 was 1966, checked in by rgrieder, 16 years ago

Let's go for multithreaded physics!

  • Property svn:eol-style set to native
File size: 16.5 KB
Line 
1/*
2Bullet Continuous Collision Detection and Physics Library - Parallel solver
3Copyright (c) 2007 Starbreeze Studios
4
5This software is provided 'as-is', without any express or implied warranty.
6In no event will the authors be held liable for any damages arising from the use of this software.
7Permission is granted to anyone to use this software for any purpose,
8including commercial applications, and to alter it and redistribute it freely,
9subject to the following restrictions:
10
111. 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.
122. Altered source versions must be plainly marked as such, and must not be misrepresented as being the original software.
133. This notice may not be removed or altered from any source distribution.
14
15Written by: Marten Svanfeldt
16*/
17
18#include "SpuParallelSolver.h"
19
20//#include "SpuFakeDma.h"
21#include "SpuSync.h"
22
23#include "LinearMath/btVector3.h"
24#include "BulletCollision/NarrowPhaseCollision/btPersistentManifold.h"
25#include "BulletDynamics/Dynamics/btRigidBody.h"
26#include "BulletDynamics/ConstraintSolver/btContactSolverInfo.h"
27#include "LinearMath/btMinMax.h"
28#include "BulletCollision/CollisionShapes/btCollisionShape.h"
29#include "BulletCollision/CollisionDispatch/btCollisionObject.h"
30#include "BulletDynamics/ConstraintSolver/btTypedConstraint.h"
31#include "LinearMath/btQuickprof.h"
32
33#include "SpuSolverTask/SpuParallellSolverTask.h"
34
35#include <stdio.h>
36
37enum
38{
39        PARALLEL_SOLVER_BODIES_PER_TASK = 64,
40        PARALLEL_SOLVER_CELLS_PER_TASK = SPU_HASH_NUMCELLS >> 3
41};
42
43
44//-- Hash handling
45static void recordDependency(SpuSolverHash* hash, unsigned int i, unsigned int j)
46{
47        hash->m_dependencyMatrix[i][j >> 5] |= (1 << (j & 31));
48        hash->m_dependencyMatrix[j][i >> 5] |= (1 << (i & 31));
49}
50
51
52// Clear the given hash
53static void clearHash (SpuSolverHash* hash)
54{
55        size_t hashSize = sizeof(SpuSolverHash);
56        memset(hash, 0, hashSize);
57        int i;
58
59        // Setup basic dependency
60        for ( i = 0; i < SPU_HASH_NUMCELLS; ++i)
61        {
62                hash->m_dependencyMatrix[i][i >> 5] |= (1 << (i & 31));
63        }
64
65        // Set some ones to "unused cells"
66        for ( i = SPU_HASH_WORDWIDTH-SPU_HASH_NUMUNUSEDBITS; i < SPU_HASH_WORDWIDTH; ++i)
67        {
68                hash->m_currentMask[0][SPU_HASH_NUMCELLDWORDS-1] |= (1 << i);
69        }
70}
71/*
72static bool getDependency(SpuSolverHash* hash, unsigned int i, unsigned int j)
73{
74        return (hash->m_dependencyMatrix[i][j >> 5] & (1 << (j & 31))) != 0;
75}
76*/
77
78
79static unsigned int getObjectIndex (btCollisionObject* object)
80{
81        btVector3 center = object->getWorldTransform().getOrigin();
82        int cx = (int)floorf(center.x() / SPU_HASH_PHYSSIZE);
83        int cy = (int)floorf(center.y() / SPU_HASH_PHYSSIZE);
84        int cz = (int)floorf(center.z() / SPU_HASH_PHYSSIZE);
85
86        return spuGetHashCellIndex(cx, cy, cz);
87};
88
89
90
91
92
93btParallelSequentialImpulseSolver::btParallelSequentialImpulseSolver (btThreadSupportInterface* threadIf, int maxOutstandingTasks)
94: m_numberOfContacts(0), m_taskScheduler (threadIf, maxOutstandingTasks)
95{
96        m_solverHash = new SpuSolverHash;
97        clearHash(m_solverHash);
98}
99
100btParallelSequentialImpulseSolver::~btParallelSequentialImpulseSolver ()
101{
102        delete m_solverHash;
103}
104
105
106void btParallelSequentialImpulseSolver::prepareSolve(int numBodies, int numManifolds)
107{
108        m_sortedManifolds.reserve(numManifolds);
109        m_allObjects.reserve(numBodies);
110}
111
112btScalar btParallelSequentialImpulseSolver::solveGroup(btCollisionObject** bodies,int numBodies,btPersistentManifold** manifold,int numManifolds,btTypedConstraint** constraints,int numConstraints, const btContactSolverInfo& info,class btIDebugDraw* debugDrawer, btStackAlloc* stackAlloc,btDispatcher* dispatcher)
113{
114        BT_PROFILE("parallel_solveGroup");
115
116        if (!numManifolds && !numConstraints)
117                return 0;
118        int i;
119
120///refresh contact points is not needed anymore, it has been moved into the processCollision detection part.
121#ifdef FORCE_REFESH_CONTACT_MANIFOLDS
122        for ( i = 0; i < numManifolds; ++i)
123        {
124                btPersistentManifold* currManifold = manifold[i];
125                btRigidBody* rb0 = (btRigidBody*)currManifold->getBody0();
126                btRigidBody* rb1 = (btRigidBody*)currManifold->getBody1();
127
128                currManifold->refreshContactPoints(rb0->getCenterOfMassTransform(),rb1->getCenterOfMassTransform());
129        }
130#endif //FORCE_REFESH_CONTACT_MANIFOLDS
131
132        // Record and mark the manifolds to the cells
133        for ( i = 0; i < numManifolds; ++i)
134        {
135                // Compute a hash cell for this manifold
136                btPersistentManifold* currManifold = manifold[i];
137
138                btCollisionObject *ownerObject, *otherObject;
139
140                btRigidBody* rb0 = (btRigidBody*)currManifold->getBody0();
141                btRigidBody* rb1 = (btRigidBody*)currManifold->getBody1();
142
143                if (rb0->getIslandTag() >= 0)
144                {
145                        ownerObject = rb0;
146                        otherObject = rb1;
147                }
148                else
149                {
150                        ownerObject = rb1;
151                        otherObject = rb0;
152                }
153
154                // Save the cell
155                unsigned int ownerCellIdx = getObjectIndex(ownerObject);
156                ManifoldCellHolder holder = {ownerCellIdx, currManifold};
157                m_sortedManifolds.push_back(holder);
158                m_solverHash->m_Hash[ownerCellIdx].m_numManifolds++;
159
160                // Record dependency
161                if (rb0->getIslandTag() >= 0 && rb1->getIslandTag() >= 0)
162                {
163                        unsigned int otherCellIdx = getObjectIndex(otherObject);
164                        recordDependency(m_solverHash, ownerCellIdx, otherCellIdx);
165                }
166               
167                // Save statistics
168                int numContacts = currManifold->getNumContacts();
169                m_solverHash->m_Hash[ownerCellIdx].m_numContacts += numContacts;
170                m_numberOfContacts += numContacts;
171        }
172
173        // Record and mark constraints to the cells
174        for ( i = 0; i < numConstraints; ++i)
175        {
176                // Compute a hash cell for this manifold
177                btTypedConstraint* currConstraint = constraints[i];
178
179                if (!constraintTypeSupported(currConstraint->getConstraintType()))
180                        continue;
181
182                btCollisionObject *ownerObject, *otherObject;
183
184                btRigidBody* rb0 = &currConstraint->getRigidBodyA();
185                btRigidBody* rb1 = &currConstraint->getRigidBodyB();
186
187                if (rb0->getIslandTag() >= 0)
188                {
189                        ownerObject = rb0;
190                        otherObject = rb1;
191                }
192                else
193                {
194                        ownerObject = rb1;
195                        otherObject = rb0;
196                }
197
198                // Save the cell
199                unsigned int ownerCellIdx = getObjectIndex(ownerObject);
200                ConstraintCellHolder holder = {ownerCellIdx, currConstraint->getConstraintType(), currConstraint};
201                m_sortedConstraints.push_back(holder);
202                m_solverHash->m_Hash[ownerCellIdx].m_numConstraints++;
203
204                // Record dependency
205                if (rb0 && rb1 && rb0->getIslandTag() >= 0 && rb1->getIslandTag() >= 0)
206                {
207                        unsigned int otherCellIdx = getObjectIndex(otherObject);
208                        recordDependency(m_solverHash, ownerCellIdx, otherCellIdx);
209                }
210        }
211
212        // Save all RBs
213        for ( i = 0; i < numBodies; ++i)
214        {
215                btCollisionObject* obj = bodies[i];
216                //unsigned int cellIdx = getObjectIndex(obj);
217
218                btRigidBody* rb = btRigidBody::upcast(obj);
219                m_allObjects.push_back(rb);
220        }
221
222        return 0;
223}
224
225template<typename T>
226class CellHolderPredicate
227{
228public:
229        SIMD_FORCE_INLINE bool operator() ( const T& lhs, const T& rhs )
230        {
231                return lhs.m_hashCellIndex < rhs.m_hashCellIndex;
232        }
233};
234
235
236/*static void printDependencyMatrix(SpuSolverHash* hash)
237{
238        for (int r = 0; r < SPU_HASH_NUMCELLS; ++r)
239        {
240                for (int c = 0; c < SPU_HASH_NUMCELLS; ++c)
241                {
242                        if (getDependency(hash, r, c))
243                        {
244                                printf("1");
245                        }
246                        else
247                        {
248                                printf("0");
249                        }
250                }
251
252                printf("\n");
253        }
254        printf("\n");
255        fflush(stdout);
256}
257*/
258
259// Solver caches
260btAlignedObjectArray<SpuSolverBody> solverBodyPool_persist;
261btAlignedObjectArray<uint32_t> solverBodyOffsetList_persist;
262btAlignedObjectArray<SpuSolverInternalConstraint> solverInternalConstraintPool_persist;
263btAlignedObjectArray<SpuSolverConstraint> solverConstraintPool_persist;
264
265
266void btParallelSequentialImpulseSolver::allSolved (const btContactSolverInfo& info,class btIDebugDraw* debugDrawer, btStackAlloc* stackAlloc)
267{
268        BT_PROFILE("parallel_allSolved");
269
270        if (!m_numberOfContacts && !m_sortedConstraints.size())
271        {
272                m_sortedManifolds.clear();
273                m_sortedConstraints.clear();
274                m_allObjects.clear();
275                clearHash(m_solverHash);
276                return;
277        }
278
279
280        //printDependencyMatrix(m_solverHash);
281
282        // Sort the manifolds list
283        int numManifolds = m_sortedManifolds.size();
284        m_sortedManifolds.quickSort(CellHolderPredicate<ManifoldCellHolder>());
285
286        // Sort the constraint list
287        int numConstraints = m_sortedConstraints.size();
288        m_sortedConstraints.quickSort(CellHolderPredicate<ConstraintCellHolder>());
289
290
291        // Sort the body list
292        int numBodies = m_allObjects.size();
293       
294        // Reassign the hash offset
295        uint32_t emptyCellMask[SPU_HASH_NUMCELLDWORDS] = {0};
296        int numBodyOffsets = 0;
297        {
298                int manifoldRunner = 0;
299                int bodyOffsetRunner = 0;
300                int internalConstraintRunner = 0;
301                int constraintRunner = 0;
302               
303                for (int i = 0; i < SPU_HASH_NUMCELLS; ++i)
304                {
305                        bool empty = true;
306
307                        SpuSolverHashCell& hashCell = m_solverHash->m_Hash[i];
308                        hashCell.m_solverBodyOffsetListOffset = bodyOffsetRunner;
309
310                        if (hashCell.m_numManifolds)
311                        {
312                                hashCell.m_manifoldListOffset = manifoldRunner;
313                                manifoldRunner += hashCell.m_numManifolds;
314                               
315                                bodyOffsetRunner += hashCell.m_numManifolds*2;
316                        }                       
317                        if (hashCell.m_numContacts)
318                        {
319                                hashCell.m_internalConstraintListOffset = internalConstraintRunner*3;
320                                internalConstraintRunner += hashCell.m_numContacts;
321                                empty = false;
322                        }
323
324                        if (hashCell.m_numConstraints)
325                        {
326                                hashCell.m_constraintListOffset = constraintRunner;
327                                constraintRunner += hashCell.m_numConstraints;
328
329                                bodyOffsetRunner += hashCell.m_numConstraints*2;
330
331                                empty = false;
332                        }
333                       
334
335                        emptyCellMask[i >> 5] |= (empty ? (1 << (i&31)) : 0);
336                        // Align the bodyOffsetRunner to a whole number of 4 for right alignment in the list
337                        bodyOffsetRunner = (bodyOffsetRunner+3)&~0x3;
338                }
339
340                numBodyOffsets = bodyOffsetRunner;
341        }
342
343        // Setup rigid bodies
344        // Allocate temporary data
345        solverBodyPool_persist.resize(numBodies + numManifolds + numConstraints);
346        SpuSolverBody* solverBodyPool = &solverBodyPool_persist[0];
347
348        solverBodyOffsetList_persist.resize(numBodyOffsets);
349        uint32_t* solverBodyOffsetList = &solverBodyOffsetList_persist[0];
350
351        solverInternalConstraintPool_persist.resize(m_numberOfContacts*3);
352        SpuSolverInternalConstraint* solverInternalConstraintPool = &solverInternalConstraintPool_persist[0];
353       
354        solverConstraintPool_persist.resize(numConstraints);
355        SpuSolverConstraint* solverConstraintPool = &solverConstraintPool_persist[0];
356
357        // Setup all the moving rigid bodies
358        {
359                BT_PROFILE("setup moving rigidbodies");
360
361                int bodiesPerTask = PARALLEL_SOLVER_BODIES_PER_TASK;
362                int bodiesToSchedule = numBodies;
363                int startBody = 0;
364
365                while (bodiesToSchedule > 0)
366                {
367                        // Schedule a bunch of hash cells
368                        int numBodiesInTask = bodiesToSchedule > bodiesPerTask ? bodiesPerTask : bodiesToSchedule;
369
370                        SpuSolverTaskDesc* desc = m_taskScheduler.getTask();
371
372                        desc->m_solverCommand = CMD_SOLVER_SETUP_BODIES;
373                        desc->m_solverData.m_solverHash = m_solverHash;
374                        desc->m_solverData.m_solverBodyList = solverBodyPool;
375
376                        desc->m_commandData.m_bodySetup.m_startBody = startBody;
377                        desc->m_commandData.m_bodySetup.m_numBodies = numBodiesInTask;
378                        desc->m_commandData.m_bodySetup.m_rbList = &m_allObjects[0];
379
380                        m_taskScheduler.issueTask();
381                        bodiesToSchedule -= numBodiesInTask;
382                        startBody += numBodiesInTask;
383                }
384               
385                m_taskScheduler.flushTasks();
386        }
387
388        // Manifold setup
389        {
390                int cellsPerTask = PARALLEL_SOLVER_CELLS_PER_TASK;
391                int cellsToSchedule = SPU_HASH_NUMCELLS;
392                int startCell = 0;
393
394                while (cellsToSchedule > 0)
395                {
396                        int numCellsInTask = cellsToSchedule > cellsPerTask ? cellsPerTask : cellsToSchedule;
397                       
398                        SpuSolverTaskDesc* desc = m_taskScheduler.getTask();
399
400                        desc->m_solverCommand = CMD_SOLVER_MANIFOLD_SETUP;
401                        desc->m_solverData.m_solverHash = m_solverHash;
402                        desc->m_solverData.m_solverBodyList = solverBodyPool;
403                        desc->m_solverData.m_solverBodyOffsetList = solverBodyOffsetList;
404                        desc->m_solverData.m_solverInternalConstraintList = solverInternalConstraintPool;
405                        desc->m_solverData.m_solverConstraintList = solverConstraintPool;
406
407                        desc->m_commandData.m_manifoldSetup.m_startCell = startCell;
408                        desc->m_commandData.m_manifoldSetup.m_numCells = numCellsInTask;
409                        desc->m_commandData.m_manifoldSetup.m_numBodies = numBodies;
410                        desc->m_commandData.m_manifoldSetup.m_numManifolds = numManifolds;
411                        desc->m_commandData.m_manifoldSetup.m_manifoldHolders = &m_sortedManifolds[0];
412                        desc->m_commandData.m_manifoldSetup.m_constraintHolders = &m_sortedConstraints[0];
413                        desc->m_commandData.m_manifoldSetup.m_solverInfo = info;
414
415                        m_taskScheduler.issueTask();
416                        cellsToSchedule -= numCellsInTask;
417                        startCell += numCellsInTask;
418                }
419                m_taskScheduler.flushTasks();
420        }
421
422        {
423                BT_PROFILE("parallel_solve_iterations");
424
425                btSpinlock::SpinVariable* spinVar = (btSpinlock::SpinVariable*)btAlignedAlloc(sizeof(btSpinlock::SpinVariable), 128);
426                for (int iter = 0; iter < info.m_numIterations; ++iter)
427                {
428                        btSpinlock lock (spinVar);
429                        lock.Init();
430
431                        // Clear the "processed cells" part of the hash
432                        memcpy(m_solverHash->m_currentMask[0], emptyCellMask, sizeof(uint32_t)*SPU_HASH_NUMCELLDWORDS);
433
434                        for (int task = 0; task < m_taskScheduler.getMaxOutstandingTasks(); ++task)
435                        {
436                                SpuSolverTaskDesc* desc = m_taskScheduler.getTask();
437                                desc->m_solverCommand = CMD_SOLVER_SOLVE_ITERATE;
438
439                                desc->m_solverData.m_solverHash = m_solverHash;
440                                desc->m_solverData.m_solverBodyList = solverBodyPool;
441                                desc->m_solverData.m_solverBodyOffsetList = solverBodyOffsetList;
442                                desc->m_solverData.m_solverInternalConstraintList = solverInternalConstraintPool;
443                                desc->m_solverData.m_solverConstraintList = solverConstraintPool;
444
445                                desc->m_commandData.m_iterate.m_spinLockVar = spinVar;
446
447                                m_taskScheduler.issueTask();
448                        } 
449                        m_taskScheduler.flushTasks();           
450                }
451                btAlignedFree((void*)spinVar);
452        }
453       
454        // Write back velocity
455        {
456                int bodiesPerTask = PARALLEL_SOLVER_BODIES_PER_TASK;
457                int bodiesToSchedule = numBodies;
458                int startBody = 0;
459
460                while (bodiesToSchedule > 0)
461                {
462                        // Schedule a bunch of hash cells
463                        int numBodiesInTask = bodiesToSchedule > bodiesPerTask ? bodiesPerTask : bodiesToSchedule;
464
465                        SpuSolverTaskDesc* desc = m_taskScheduler.getTask();
466
467                        desc->m_solverCommand = CMD_SOLVER_COPYBACK_BODIES;
468                        desc->m_solverData.m_solverHash = m_solverHash;
469                        desc->m_solverData.m_solverBodyList = solverBodyPool;
470
471                        desc->m_commandData.m_bodyCopyback.m_startBody = startBody;
472                        desc->m_commandData.m_bodyCopyback.m_numBodies = numBodiesInTask;
473                        desc->m_commandData.m_bodyCopyback.m_rbList = &m_allObjects[0];
474
475                        m_taskScheduler.issueTask();
476                        bodiesToSchedule -= numBodiesInTask;
477                        startBody += numBodiesInTask;
478                }
479
480                m_taskScheduler.flushTasks();
481        }
482
483
484        // Clean up
485        m_sortedManifolds.resize(0);
486        m_sortedConstraints.resize(0);
487        m_allObjects.resize(0);
488        clearHash(m_solverHash);
489
490
491        m_numberOfContacts = 0;
492}
493
494void btParallelSequentialImpulseSolver::reset()
495{
496        m_sortedManifolds.clear();
497        m_allObjects.clear();
498        m_numberOfContacts = 0;
499        clearHash(m_solverHash);
500
501        solverBodyPool_persist.clear();
502        solverBodyOffsetList_persist.clear();
503        solverConstraintPool_persist.clear();
504        solverInternalConstraintPool_persist.clear();
505}
506
507
508SolverTaskScheduler::SolverTaskScheduler(btThreadSupportInterface* threadIf, int maxOutstandingTasks)
509: m_threadInterface (threadIf), m_maxNumOutstandingTasks (maxOutstandingTasks > SPU_MAX_SPUS ? SPU_MAX_SPUS : maxOutstandingTasks), 
510m_currentTask (0), m_numBusyTasks (0)
511{
512        m_taskDescriptors.resize(m_maxNumOutstandingTasks);
513        m_taskBusy.resize(m_maxNumOutstandingTasks);
514
515        m_threadInterface->startSPU();
516}
517
518
519SolverTaskScheduler::~SolverTaskScheduler()
520{
521        m_threadInterface->stopSPU();
522}
523
524SpuSolverTaskDesc* SolverTaskScheduler::getTask()
525{
526        int taskIdx = -1;
527
528        if (m_taskBusy[m_currentTask])
529        {
530                //try to find a new one
531                for (int i = 0; i < m_maxNumOutstandingTasks; ++i)
532                {
533                        if (!m_taskBusy[i])
534                        {
535                                taskIdx = i;
536                                break;
537                        }
538                }
539
540                if (taskIdx < 0)
541                {
542                        // Have to wait
543                        unsigned int taskId;
544                        unsigned int outputSize;
545
546                        for (int i=0;i<m_maxNumOutstandingTasks;i++)
547                          {
548                                  if (m_taskBusy[i])
549                                  {
550                                          taskId = i;
551                                          break;
552                                  }
553                          }
554
555                        m_threadInterface->waitForResponse(&taskId, &outputSize);
556
557                        m_taskBusy[taskId] = false;
558                        m_numBusyTasks--;
559
560                        taskIdx = taskId;
561                }
562
563                m_currentTask = taskIdx;
564        }
565
566
567        SpuSolverTaskDesc* result = &m_taskDescriptors[m_currentTask];
568        memset(result, 0, sizeof(SpuSolverTaskDesc));
569        result->m_taskId = m_currentTask;
570
571        return result;
572}
573
574void SolverTaskScheduler::issueTask()
575{
576        m_taskBusy[m_currentTask] = true;
577        m_numBusyTasks++;
578
579        SpuSolverTaskDesc& desc = m_taskDescriptors[m_currentTask];
580       
581        m_threadInterface->sendRequest(1, (uint32_t)&desc, m_currentTask);
582}
583
584void SolverTaskScheduler::flushTasks()
585{
586        while (m_numBusyTasks > 0)
587        {
588                unsigned int taskId;
589                unsigned int outputSize;
590                for (int i=0;i<m_maxNumOutstandingTasks;i++)
591          {
592                  if (m_taskBusy[i])
593                  {
594                          taskId = i;
595                          break;
596                  }
597          }
598
599                m_threadInterface->waitForResponse(&taskId, &outputSize);
600
601                m_taskBusy[taskId] = false;
602                m_numBusyTasks--;
603        }
604}
Note: See TracBrowser for help on using the repository browser.