btSolverBody implementation question

RobW
Posts: 33
Joined: Fri Feb 01, 2008 9:44 am

btSolverBody implementation question

Post by RobW »

Hi,

In the following code (from btSequentialImpulseConstraintSolver::solveGroupCacheFriendlySetup)...

Code: Select all

					if (colObj0->getIslandTag() >= 0)
					{
						if (colObj0->getCompanionId() >= 0)
						{
							//body has already been converted
							solverBodyIdA = colObj0->getCompanionId();
						} else
						{
							solverBodyIdA = m_tmpSolverBodyPool.size();
							btSolverBody& solverBody = m_tmpSolverBodyPool.expand();
							initSolverBody(&solverBody,colObj0);
							colObj0->setCompanionId(solverBodyIdA);
						}
					} else
					{
						//create a static body
						solverBodyIdA = m_tmpSolverBodyPool.size();
						btSolverBody& solverBody = m_tmpSolverBodyPool.expand();
						initSolverBody(&solverBody,colObj0);
					}
It would appear that for bodies with an island id of -1 (static / kinematic objects) a new solver body will be created for each manifold they are involved in (so if you have 10 boxes resting on a ground plane, there will be 20 solver bodies, 10 boxes and 10 ground planes).

However, all the solver bodies write back to the body that they represent, at the end of the constraint solver. So if there was any meaningful data in the solver bodies for these objects, the last one would 'win'. On the other hand, since these objects (static objects, at least) have zero mass and a zero inertia tensor, their velocities shouldn't have been changed during the constraint solver anyway. Maybe the situation is different for kinematic objects?

I'd like to make a change such that every body gets a single solver body, and everything works on these solver bodies including all the non-contact/friction constraints. This will save various writing to/from bodies and solver bodies. Based on the above it should also reduce the size of the solver body array most of the time, and improve performance in that way too.

I just need to understand whether this is some subtle reason for the behaviour of the above code :)

EDIT: Is the issue simply that because 'm_companionId' isn't cleared on a per-island basis, but the objects in question span multiple islands? (The issue if you're solving islands on different threads is more serious!).

Many thanks,
Rob
User avatar
Erwin Coumans
Site Admin
Posts: 4221
Joined: Sun Jun 26, 2005 6:43 pm
Location: California, USA

Re: btSolverBody implementation question

Post by Erwin Coumans »

Indeed, there is room for improvements and optimizations. We haven't been optimizing btSequentialImpulseConstraintSolver, because we work on new faster parallel constraint solver. This will be open sourced once we sorted the remaining issues (please don't ask when).

First of all, we should only write back velocity for moving objects. This condition (getIslandTag>=0 and getCompanionId>= 0) is already done when writing back body information for constraints:

Code: Select all

for (j=0;j<numConstraints;j++)
			{
				btTypedConstraint* constraint = constraints[j];
				///todo: use solver bodies, so we don't need to copy from/to btRigidBody

				if ((constraint->getRigidBodyA().getIslandTag() >= 0) && (constraint->getRigidBodyA().getCompanionId() >= 0))
				{
We could leave out the second check (getCompanionId) , and check only for getIslandTag>=0 to determine moving objects. Then, we don't need duplicated solver bodies for static objects.
Obviously, constraints should directly use solver bodies, see the todo in the code.

If you have some good non-intrusive optimizations for the btSequentialImpulseConstraintSolver, we welcome your contribution.
Thanks,
Erwin
RobW
Posts: 33
Joined: Fri Feb 01, 2008 9:44 am

Re: btSolverBody implementation question

Post by RobW »

Thanks Erwin,

Yes, it started to dawn on me that the reason for duplicating solver bodies for static objects was that they would span islands and although in the strictly single threaded sense, you could just clear the companion id at the start of the island solve, but I solve islands in parallel on different threads so the problem is more complicated...and the current solution is a resonably good way of avoiding contending writes to the btRigidBody.

What is the general method you have in mind for a new parallel constraint solver? I spent some time thinking about approximating a minimum cut through the constraint graph, solving the separate sets in parallel and finally solving the cut constraints after all threads have finished. The cut would probably be derived from the spatial distribution of the bodies. However, the downside is that this would bias the order of constraint solving and undo the effect of the current randomisation (well, the cut constraints would always be solved after the disjoint ones...).

For now, I just solve separate islands in parallel but I load balance it by grouping small islands into a bigger job so that I don't spawn an excessive number of threads...constraint solving isn't actually the bottleneck in my application I'm just interested in making it faster yet.

A good non-intrusive improvement would probably be to write a separate version of solveConstraint() for each constraint type, which works on solver bodies, leaving the original method as it is. That way we get some duplicate code but it wouldn't force the use of solver bodies onto all other existing code.