Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Crash afer overflowing maxSolverFrictionProgress (PxU16) #326

Open
ikkah opened this issue Nov 8, 2024 · 2 comments
Open

Crash afer overflowing maxSolverFrictionProgress (PxU16) #326

ikkah opened this issue Nov 8, 2024 · 2 comments

Comments

@ikkah
Copy link

ikkah commented Nov 8, 2024

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
Copy link
Collaborator

Hi @ikkah, thanks for reporting.

@ikkah
Copy link
Author

ikkah commented Nov 11, 2024

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

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

2 participants