Open ikkah opened 2 weeks ago
Hi @ikkah, thanks for reporting.
Looks like I can reproduce the crash with this modified version of SnippetContactReportCCD ("checked" build config, MSVC).
It's a slightly ridiculous setup with a million triangle meshes, but not that different from the actual use-case I have, to be honest.
#include <vector>
#include "PxPhysicsAPI.h"
#include "../snippetutils/SnippetUtils.h"
#include "../snippetcommon/SnippetPrint.h"
#include "../snippetcommon/SnippetPVD.h"
using namespace physx;
static PxDefaultAllocator gAllocator;
static PxDefaultErrorCallback gErrorCallback;
static PxFoundation* gFoundation = NULL;
static PxPhysics* gPhysics = NULL;
static PxDefaultCpuDispatcher* gDispatcher = NULL;
static PxScene* gScene = NULL;
static PxMaterial* gMaterial = NULL;
static PxTriangleMesh* gTriangleMesh = NULL;
static PxRigidStatic* gTriangleMeshActor = NULL;
static PxRigidDynamic* gSphereActor = NULL;
static PxPvd* gPvd = NULL;
static PxU32 gSimStepCount = 0;
std::vector<PxVec3> gContactPositions;
std::vector<PxVec3> gContactImpulses;
std::vector<PxVec3> gContactSphereActorPositions;
static PxFilterFlags contactReportFilterShader( PxFilterObjectAttributes attributes0, PxFilterData filterData0,
PxFilterObjectAttributes attributes1, PxFilterData filterData1,
PxPairFlags& pairFlags, const void* constantBlock, PxU32 constantBlockSize)
{
PX_UNUSED(attributes0);
PX_UNUSED(attributes1);
PX_UNUSED(filterData0);
PX_UNUSED(filterData1);
PX_UNUSED(constantBlockSize);
PX_UNUSED(constantBlock);
//
// Enable CCD for the pair, request contact reports for initial and CCD contacts.
// Additionally, provide information per contact point and provide the actor
// pose at the time of contact.
//
pairFlags = PxPairFlag::eCONTACT_DEFAULT
| PxPairFlag::eDETECT_CCD_CONTACT
| PxPairFlag::eNOTIFY_TOUCH_CCD
| PxPairFlag::eNOTIFY_TOUCH_FOUND
| PxPairFlag::eNOTIFY_CONTACT_POINTS
| PxPairFlag::eCONTACT_EVENT_POSE;
return PxFilterFlag::eDEFAULT;
}
class ContactReportCallback: public PxSimulationEventCallback
{
void onConstraintBreak(PxConstraintInfo* constraints, PxU32 count) { PX_UNUSED(constraints); PX_UNUSED(count); }
void onWake(PxActor** actors, PxU32 count) { PX_UNUSED(actors); PX_UNUSED(count); }
void onSleep(PxActor** actors, PxU32 count) { PX_UNUSED(actors); PX_UNUSED(count); }
void onTrigger(PxTriggerPair* pairs, PxU32 count) { PX_UNUSED(pairs); PX_UNUSED(count); }
void onAdvance(const PxRigidBody*const*, const PxTransform*, const PxU32) {}
void onContact(const PxContactPairHeader& pairHeader, const PxContactPair* pairs, PxU32 nbPairs)
{
std::vector<PxContactPairPoint> contactPoints;
PxTransform spherePose(PxIdentity);
PxU32 nextPairIndex = 0xffffffff;
PxContactPairExtraDataIterator iter(pairHeader.extraDataStream, pairHeader.extraDataStreamSize);
bool hasItemSet = iter.nextItemSet();
if (hasItemSet)
nextPairIndex = iter.contactPairIndex;
for(PxU32 i=0; i < nbPairs; i++)
{
//
// Get the pose of the dynamic object at time of impact.
//
if (nextPairIndex == i)
{
if (pairHeader.actors[0]->is<PxRigidDynamic>())
spherePose = iter.eventPose->globalPose[0];
else
spherePose = iter.eventPose->globalPose[1];
gContactSphereActorPositions.push_back(spherePose.p);
hasItemSet = iter.nextItemSet();
if (hasItemSet)
nextPairIndex = iter.contactPairIndex;
}
//
// Get the contact points for the pair.
//
const PxContactPair& cPair = pairs[i];
if (cPair.events & (PxPairFlag::eNOTIFY_TOUCH_FOUND | PxPairFlag::eNOTIFY_TOUCH_CCD))
{
PxU32 contactCount = cPair.contactCount;
contactPoints.resize(contactCount);
cPair.extractContacts(&contactPoints[0], contactCount);
for(PxU32 j=0; j < contactCount; j++)
{
gContactPositions.push_back(contactPoints[j].position);
gContactImpulses.push_back(contactPoints[j].impulse);
}
}
}
}
};
ContactReportCallback gContactReportCallback;
static void initScene()
{
//
// Create a static triangle mesh
//
srand( 1337 );
std::vector< PxVec3 > vertices;
for( int i = 0; i != 500; ++i )
{
vertices.push_back( PxVec3( ( std::rand() % 1000 ) * 0.01f, ( std::rand() % 100 ) * 0.01f, ( std::rand() % 10 ) * 0.01f ) );
}
PxU32 vertexCount = PxU32( vertices.size() );
std::vector< PxU32 > triangleIndices;
for( int i = 0; i != 1000; ++i )
{
triangleIndices.push_back( ( i + 0 ) % vertices.size() );
triangleIndices.push_back( ( i + 1 ) % vertices.size() );
triangleIndices.push_back( ( i + 2 ) % vertices.size() );
}
PxU32 triangleCount = PxU32( triangleIndices.size() );
PxTriangleMeshDesc triangleMeshDesc;
triangleMeshDesc.points.count = vertexCount;
triangleMeshDesc.points.data = vertices.data();
triangleMeshDesc.points.stride = sizeof(PxVec3);
triangleMeshDesc.triangles.count = triangleCount;
triangleMeshDesc.triangles.data = triangleIndices.data();
triangleMeshDesc.triangles.stride = 3 * sizeof(PxU32);
PxTolerancesScale tolerances;
const PxCookingParams params(tolerances);
gTriangleMesh = PxCreateTriangleMesh(params, triangleMeshDesc, gPhysics->getPhysicsInsertionCallback());
if (!gTriangleMesh)
return;
for( int i = 0; i != 1000 * 1000; ++i )
{
gTriangleMeshActor = gPhysics->createRigidStatic(PxTransform(PxVec3(0.0f, 1.0f, 0.0f), PxQuat(PxHalfPi / 60.0f, PxVec3(0.0f, 1.0f, 0.0f))));
PxTriangleMeshGeometry triGeom(gTriangleMesh);
PxShape* triangleMeshShape = PxRigidActorExt::createExclusiveShape(*gTriangleMeshActor, triGeom, *gMaterial);
if (!triangleMeshShape)
return;
gScene->addActor(*gTriangleMeshActor);
}
//
// Create a fast moving sphere that will hit and bounce off the static triangle mesh 3 times
// in one simulation step.
//
PxTransform spherePose(PxVec3(0.0f, 5.0f, 1.0f));
gContactSphereActorPositions.push_back(spherePose.p);
gSphereActor = gPhysics->createRigidDynamic(spherePose);
gSphereActor->setRigidBodyFlag(PxRigidBodyFlag::eENABLE_SPECULATIVE_CCD, true);
if (!gSphereActor)
return;
PxBoxGeometry sphereGeom(1.0f, 1.0f, 1.0f);
PxShape* sphereShape = PxRigidActorExt::createExclusiveShape(*gSphereActor, sphereGeom, *gMaterial);
if (!sphereShape)
return;
PxRigidBodyExt::updateMassAndInertia(*gSphereActor, 1.0f);
PxReal velMagn = 900.0f;
PxVec3 vel = PxVec3(-1.0f, -1.0f, 0.0f);
vel.normalize();
vel *= velMagn;
gSphereActor->setLinearVelocity(vel);
gSphereActor->setAngularVelocity(vel);
gScene->addActor(*gSphereActor);
}
void initPhysics(bool /*interactive*/)
{
gFoundation = PxCreateFoundation(PX_PHYSICS_VERSION, gAllocator, gErrorCallback);
gPvd = PxCreatePvd(*gFoundation);
PxPvdTransport* transport = PxDefaultPvdSocketTransportCreate(PVD_HOST, 5425, 10);
gPvd->connect(*transport,PxPvdInstrumentationFlag::eALL);
gPhysics = PxCreatePhysics(PX_PHYSICS_VERSION, *gFoundation, PxTolerancesScale(), true, gPvd);
PxInitExtensions(*gPhysics, gPvd);
PxU32 numCores = SnippetUtils::getNbPhysicalCores();
gDispatcher = PxDefaultCpuDispatcherCreate(numCores == 0 ? 0 : numCores - 1);
PxSceneDesc sceneDesc(gPhysics->getTolerancesScale());
sceneDesc.cpuDispatcher = gDispatcher;
sceneDesc.gravity = PxVec3(0, 0, 0);
sceneDesc.filterShader = contactReportFilterShader;
sceneDesc.simulationEventCallback = &gContactReportCallback;
sceneDesc.flags |= PxSceneFlag::eENABLE_CCD;
sceneDesc.ccdMaxPasses = 4;
gScene = gPhysics->createScene(sceneDesc);
PxPvdSceneClient* pvdClient = gScene->getScenePvdClient();
if(pvdClient)
{
pvdClient->setScenePvdFlag(PxPvdSceneFlag::eTRANSMIT_CONTACTS, true);
}
gMaterial = gPhysics->createMaterial(0.5f, 0.5f, 1.0f);
initScene();
}
void stepPhysics(bool /*interactive*/)
{
if (!gSimStepCount)
{
gScene->simulate(1.0f/60.0f);
gScene->fetchResults(true);
printf("%d contact points\n", PxU32(gContactPositions.size()));
if (gSphereActor)
gContactSphereActorPositions.push_back(gSphereActor->getGlobalPose().p);
gSimStepCount = 1;
}
}
void cleanupPhysics(bool /*interactive*/)
{
PX_RELEASE(gSphereActor);
PX_RELEASE(gTriangleMeshActor);
PX_RELEASE(gTriangleMesh);
PX_RELEASE(gScene);
PX_RELEASE(gDispatcher);
PxCloseExtensions();
PX_RELEASE(gPhysics);
if(gPvd)
{
PxPvdTransport* transport = gPvd->getTransport();
PX_RELEASE(gPvd);
PX_RELEASE(transport);
}
PX_RELEASE(gFoundation);
printf("SnippetContactReportCCD done.\n");
}
int snippetMain(int, const char*const*)
{
#ifdef RENDER_SNIPPET
extern void renderLoop();
renderLoop();
#else
initPhysics(false);
stepPhysics(false);
cleanupPhysics(false);
#endif
return 0;
}
Library and Version
v5.4.2-106.1 (latest available)
Also reproduced with 5.3.1.
Operating System
Windows 10
Steps to Trigger Behavior
Unfortunately I don't have a test case I could share, but the issue occurs in situations where more than 2^16 contacts are generated.
Articulations are not used in the scene.
Expected Behavior
I'd expect PhysX to error or warn without crashing, possibly omitting the contacts over 2^16.
Actual Behavior
There is an overflow of maxSolverFrictionProgress in recordStaticConstraint() (DyConstraintPartition.cpp).
PhysX eventually crashes with the following call stack:
I couldn't quite figure out the connection between the overflow and the subsequent crash, but the crash seems to happen every time after the overflow.
Please let me know if you need more information.