PhysX
PhysX copied to clipboard
Crash inside NpConstraint::updateConstants
Library and Version
PhysX v5.3.0 (last commit on trunk)
Operating System
Windows 10
Steps to Trigger Behavior
- Create a RigidBody & a constraint (like a joint)
- Call
immediate::PxCreateJointConstraintsWithShaders
- Release the constraint
- Simulate
A new constraint will be marked dirty (flag ConstraintCore::mIsDirty
) and will be registered inside NpScene::mDirtyConstraints
.
Both array and flag are cleaned inside a simulation (NpScene::updateDirtyShaders
).
The call to immediate::PxCreateJointConstraintsWithShaders
will trigger a call to PxConstraintAdapter::getData
causing the constraint to be marked as clean but not removed from the dirty array of the scene.
Code Snippet to Reproduce Behavior
There might be some useless code here. But from HelloWorldSnippet inside InitPhysics
:
// Step 1
PxRigidDynamic* spherePhysic = PxCreateDynamic( *gPhysics, PxTransform( 0.f, 0.f, 0.f ), PxSphereGeometry( 3.0f ), *gMaterial, 1.f );
gScene->addActor( *spherePhysic );
PxFixedJoint* joint = PxFixedJointCreate( *gPhysics, spherePhysic, PxTransform( PxIDENTITY::PxIdentity ), NULL, PxTransform( PxIDENTITY::PxIdentity ) );
// Step 2
{
immediate::PxRigidBodyData rigidBodyData;
rigidBodyData.invMass = spherePhysic->getInvMass();
rigidBodyData.invInertia = spherePhysic->getMassSpaceInvInertiaTensor();
rigidBodyData.maxDepenetrationVelocity = spherePhysic->getMaxDepenetrationVelocity();
rigidBodyData.maxContactImpulse = spherePhysic->getMaxContactImpulse();
rigidBodyData.linearDamping = spherePhysic->getLinearDamping();
rigidBodyData.angularDamping = spherePhysic->getAngularDamping();
rigidBodyData.maxLinearVelocitySq = 100.f * 100.f;
rigidBodyData.maxAngularVelocitySq = spherePhysic->getMaxAngularVelocity() * spherePhysic->getMaxAngularVelocity();
PX_ALIGN_PREFIX(16) PxSolverBody oSolverBody;
PxSolverBodyData solverBodyData;
PxSolverBodyData solverStaticData;
physx::immediate::PxConstructStaticSolverBody( PxTransform( PxIdentity ), solverStaticData );
PX_ALIGN_PREFIX( 16 ) PxSolverBody solverStatic;
memset( &solverStatic, 0, sizeof( PxSolverBody ) );
PxConstraintBatchHeader constraintBatchHeader;
constraintBatchHeader.startIndex = 0;
constraintBatchHeader.stride = 1;
constraintBatchHeader.constraintType = PxSolverConstraintDesc::eJOINT_CONSTRAINT; // Not used, will be overwritten in PxCreateJointConstraints as our constraint is not batchable
PxConstraint* constraint = joint->getConstraint();
PxSolverConstraintDesc solverConstraintDesc;
memset( &solverConstraintDesc,0, sizeof( PxSolverConstraintDesc ) );
solverConstraintDesc.bodyA = &oSolverBody;
solverConstraintDesc.bodyB = &solverStatic;
solverConstraintDesc.bodyADataIndex = 0;
solverConstraintDesc.bodyBDataIndex = 1; // Won't be used
solverConstraintDesc.linkIndexA = PxSolverConstraintDesc::RIGID_BODY;
solverConstraintDesc.linkIndexB = PxSolverConstraintDesc::RIGID_BODY;
solverConstraintDesc.constraint = reinterpret_cast<PxU8*>( constraint );
PxSolverConstraintPrepDesc solverConstraintPrepDesc;
memset( &solverConstraintPrepDesc,0, sizeof( PxSolverConstraintPrepDesc ) );
solverConstraintPrepDesc.body0 = solverConstraintDesc.bodyA;
solverConstraintPrepDesc.body1 = solverConstraintDesc.bodyB;
solverConstraintPrepDesc.data0 = &solverBodyData;
solverConstraintPrepDesc.data1 = &solverStaticData;
solverConstraintPrepDesc.bodyFrame1 = solverStaticData.body2World;
solverConstraintPrepDesc.bodyState0 = PxSolverConstraintPrepDescBase::eDYNAMIC_BODY;
solverConstraintPrepDesc.bodyState1 = PxSolverConstraintPrepDescBase::eSTATIC_BODY;
solverConstraintPrepDesc.desc = &solverConstraintDesc;
solverConstraintPrepDesc.invMassScales.angular0 = solverConstraintPrepDesc.invMassScales.angular1 = solverConstraintPrepDesc.invMassScales.linear0 =
solverConstraintPrepDesc.invMassScales.linear1 = 1.f;
solverConstraintPrepDesc.writeback = NULL;
constraint->getBreakForce( solverConstraintPrepDesc.linBreakForce, solverConstraintPrepDesc.angBreakForce );
solverConstraintPrepDesc.minResponseThreshold = constraint->getMinResponseThreshold();
solverConstraintPrepDesc.disablePreprocessing = !!( constraint->getFlags() & PxConstraintFlag::eDISABLE_PREPROCESSING );
solverConstraintPrepDesc.improvedSlerp = !!( constraint->getFlags() & PxConstraintFlag::eIMPROVED_SLERP );
solverConstraintPrepDesc.driveLimitsAreForces = !!( constraint->getFlags() & PxConstraintFlag::eDRIVE_LIMITS_ARE_FORCES );
physx::SnippetImmUtils::TestConstraintAllocator allocator;
rigidBodyData.linearVelocity = spherePhysic->getLinearVelocity();
rigidBodyData.angularVelocity = spherePhysic->getAngularVelocity();
rigidBodyData.body2World = spherePhysic->getGlobalPose();
immediate::PxConstructSolverBodies( &rigidBodyData, &solverBodyData, 1, PxVec3( 0.f, 0.f, 0.f ), 0.16f );
solverConstraintPrepDesc.bodyFrame0 = solverBodyData.body2World;
PxConstraint* oConstraintArray[1] = { constraint };
immediate::PxCreateJointConstraintsWithShaders( &constraintBatchHeader, 1, oConstraintArray, &solverConstraintPrepDesc, allocator, 0.16f,
1.f / 0.16f );
}
// Step 3
joint->release();
spherePhysic->release();
// Step 4
gScene->simulate( 1.0f / 60.0f );
gScene->fetchResults( true );
Expected Behavior
There is 2 options here.
- Remove the
NpConstraint::markClean
insidePxConstraintAdapter::getData
. This will probably cause a 2nd call toprepareData
in the simulation. - Remove from
NpScene::mDirtyConstraints
as we lower the dirty flag. Doing this will result in a different "cleaning" code. This will not make all the calls that are insideNpConstraint::updateConstants
(PVD update, andPxsSimulationController::updateJoint
).
We are not actually sure of what option is the best. Is our usage of PxCreateJointConstraintsWithShaders
right (in term of API calls)?
Actual Behavior
Dirty bool and array are not in sync
Thank you for the great report!
Internal tracking: PX-4630