NVIDIA-Omniverse / PhysX

NVIDIA PhysX SDK
BSD 3-Clause "New" or "Revised" License
2.62k stars 376 forks source link

Crash afer overflowing maxSolverFrictionProgress (PxU16) #326

Open ikkah opened 2 weeks ago

ikkah commented 2 weeks ago

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:

    PhysX_64.dll!physx::Dy::`anonymous namespace'::writeConstraintDesc<physx::Dy::`anonymous namespace'::RigidBodyClassification>(const physx::PxSolverConstraintDesc * descs, unsigned int numConstraints, physx::Dy::`anonymous-namespace'::RigidBodyClassification & classification, physx::PxArray<unsigned int,physx::PxReflectionAllocator<unsigned int>> & accumulatedConstraintsPerPartition, physx::PxSolverConstraintDesc * eaTempConstraintDescriptors, physx::PxSolverConstraintDesc * eaOrderedConstraintDesc, unsigned int maxPartitions, unsigned int numOverflows) Line 615 C++
>   PhysX_64.dll!physx::Dy::batchConstraints<0,physx::Dy::`anonymous namespace'::RigidBodyClassification>(const physx::PxSolverConstraintDesc * eaConstraintDescriptors, unsigned int numConstraintDescriptors, physx::Dy::`anonymous-namespace'::RigidBodyClassification & classification, physx::PxArray<unsigned int,physx::PxReflectionAllocator<unsigned int>> & constraintsPerPartition, physx::PxSolverConstraintDesc * eaOverflowConstraintDescriptors, unsigned int maxPartitions, physx::PxSolverConstraintDesc * eaOrderedConstraintDescriptors, unsigned int & numOverflows, unsigned int & numOrderedConstraints, unsigned int & numStaticConstraints) Line 845    C++
    PhysX_64.dll!physx::Dy::partitionContactConstraints(physx::Dy::ConstraintPartitionOut & out, const physx::Dy::ConstraintPartitionIn & in) Line 878  C++
    PhysX_64.dll!physx::Dy::PartitionTask::runInternal() Line 2254  C++

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.

vreutskyy commented 2 weeks ago

Hi @ikkah, thanks for reporting.

ikkah commented 2 weeks ago

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;
}