NVIDIAGameWorks/PhysX

Memory leakage while removing and adding many actors

Opened this issue · 4 comments

Just like the title says - I am having an issue with calling PxScene::removeActor and then PxScene::addActor - it appears the the SDK doesn't clear the memory for the object and when doing this many times I see that my program's memory footprint grows unbounded. I've tried several other methods of removing and adding actors from the scene but none have worked properly - this method is the only one that did not generate exceptions.

I'm using an array to store the PxRigidDynamic pointers and control the actors during the simulation - is this the wrong method?

Some snippets here:

PxRigidDynamic*** allActors = NULL;  //track actors for movement updates and removal/addition to the scene
bool** actorStatus = NULL;           //prevent actions on actors that have been removed

I am calling this code to create actors on startup and during the simulation:

void PhysxLib::createActor(PxTransform position, float mass, PxConvexMesh* convmesh, int i, int j)
{
    PxShape* convMeshShape = gPhysics->createShape(PxConvexMeshGeometry(convmesh), *entMat);
    PxRigidDynamic* body = gPhysics->createRigidDynamic(position);
    //bodies[i] = body;
    PxConvexMeshGeometry convGeom;
    convGeom.convexMesh = convmesh;
    convMeshShape = PxRigidActorExt::createExclusiveShape(*body, convGeom, *entMat);
    PxRigidBodyExt::updateMassAndInertia(*body, mass);

    setupFiltering(body, (1 << 0), (1 << 1), i,j,convMeshShape);
    allActors[i][j] = body;
    gScene->addActor(*body);
    convMeshShape->release();
}

These methods add and remove actors from the scene - I use them one-for-one so no extra actors should be created.
I've left in some of the other options I attempted unsuccessfully - commented code:

void PhysxLib::killList(std::vector < std::tuple<int, int> > killIndices)
{
    for (int i = 0; i < killIndices.size(); i++)
    {
        auto cur = killIndices[i];
        int GID = std::get<0>(cur);
        int EID = std::get<1>(cur);
        actorStatus[GID][EID] = false;

        gScene->removeActor(*allActors[GID][EID]);

      /*allActors[GID][EID]->setGlobalPose(PxTransform(purgatory));
        allActors[GID][EID]->setLinearVelocity(PxVec3(0, 0, 0));
        purgCur = (purgCur + purgInc > purgBound) ? purgStart : purgCur + purgBound;
        purgatory = PxVec3(purgCur, purgCur, purgCur);  */

        //allActors[GID][EID]->setActorFlag(PxActorFlag::eDISABLE_SIMULATION, true);
        
        //allActors[GID][EID]->release();
    }
}

void PhysxLib::resList(std::vector < std::tuple<int, int, Vector3, Vector3> > resIndices)
{
    for (int i = 0; i < resIndices.size(); i++)
    {
        auto cur = resIndices[i];
        int GID = std::get<0>(cur);
        int EID = std::get<1>(cur);
        Vector3 newPos = std::get<2>(cur);
        Vector3 newRot = std::get<3>(cur);

        if(actorStatus[GID][EID] == false)
        {
            PxTransform pose;
            pose.p = PxVec3(newPos.X, newPos.Y, newPos.Z);
            pose.q = PxQuat(0, PxVec3(newRot.X, newRot.Y, newRot.Z));
            actorStatus[GID][EID] = true;
            allActors[GID][EID]->setGlobalPose(pose);

            createActor(pose, AllEntities[GID].get(EID)->params.MASS, meshes[GID], GID, EID);

            //resetActor(pose, AllEntities[GID].get(EID)->params.MASS, meshes[GID], GID, EID);
          /*  allActors[GID][EID]->setActorFlag(PxActorFlag::eDISABLE_SIMULATION, false);
            allActors[GID][EID]->wakeUp();*/
        }
        
    }
    //gScene->flushSimulation();
}

I am attempting to create a long-running simulation that requires actors to be removed and later added to the scene dynamically, but so far I haven't found a good way to achieve this. The scene is bounded by planes to contain the actors as they move about the scene, and as you can see when I attempted to just move them out of the boundaries into another plane-bounded region ("purgatory"), they ended up moving rapidly outside of all bounds. Using the PxActorFlag::eDISABLE_SIMULATION would do what I need, however when I tried to remove the flag I got an exception: reference counting error. Please let me know of any methods that I can use to improve this issue, I'm out of ideas at the moment. Thank you!

Are you calling simulate() and fetchResults() at any point during the simulation?

Are you calling simulate() and fetchResults() at any point during the simulation?

No, I avoid making any API calls, or running any code at all during the simulation for now. The method:

void PhysxLib::runSim() 
{
    gScene->simulate(1.0f / 60.0f);
    gScene->fetchResults(true);
    gScene->getSimulationStatistics(stats);
}

Is being called by my main() function in my library test module, then killList(), then resList().

I think the issue is that we are buffering up deferred deletion lists when you remove actors rather than eagerly removing them from the scene. The slot in memory pools that the actor occupied becomes available to use after the next simulation step once all the internal references to that body have been purged.

If you do not intend on calling simulate() at all, then are you just reliant on scene queries only? If so, then you could probably flag your actors as "disable simulation" and just raise the flag on the shapes to act as a scene-query shape (no eSIMULATION_SHAPE flag). Then adding and removing them from the scene should potentially avoid some allocations.

You can also potentially intermittently call simulate() and fetchResults(). If there are no simulation actors/shapes in the scene, calling simulate() and fetchResults() should be extremely low overhead, if it turns out that calls are still required to make sure that deferred deletion is flushed through the system.

I think I responded too quickly to your post above - I am in fact calling simulate and fetchresults in a while loop. The order of operations is simulate(), fetchresults(), killList(), resList(), and then other functions to add forces, retrieve positions, etc. So I am calling simulate() and fetchresults() synchronously, but not asynchronously with any other code. As you can see above, I did try calling PxScene.flushSimulation() after each insertion, but that had no effect.

I do like the idea of setting the eSIMULATION_SHAPE flag - I'm looking at how to implement that change. Would it also cause the reference count to decrease like PxActorFlag::eDISABLE_SIMULATION?