The PhysX API

The API

The PhysX SDK exposes an application programming interface (API) composed primarily of abstract interface classes. The header files that belong to the public API are located in a top level directory named "Include". Classes, enumerations and functions defined by the public API have the prefix Px. We are committed to keeping this API stable and backwards-compatible from one minor release to the next, to protect the investment you make in your integration code.

Note

There is currently one section of the public API which does not have the Px prefix: PhysX Visual Debugger connection library which has the prefix Pvd.

The PhysX libraries also expose some classes and functions that are not part of the public API, such as container and platform abstractions that we find practical to reuse in our samples. These are largely undocumented, and can be recognized because they do not have the Px prefix of the public API. While they are technically available to users, most users either will not need them or will have their own versions, and we are not committed to backwards-compatibility of this code between PhysX versions.

Memory Management

PhysX relies on the application for all memory allocation. The primary interface is via the PxAllocatorCallback interface required to initialize the SDK:

class PxAllocatorCallback
{
public:
        virtual ~PxAllocatorCallback() {}
        virtual void* allocate(size_t size, const char* typeName, const char* filename, int line) = 0;
        virtual void deallocate(void* ptr) = 0;
};

The three unused parameters to allocate() are an identifier which identifies the type of allocation, and the __FILE__ and __LINE__ location inside the SDK code where the allocation was made. Refer to PxAllocatorCallback::allocate() to find out more about them. You can also obtain detailed information about memory allocation in the PhysX Visual Debugger. Configure memory profiling by setting the trackOutstandingAllocations flag when calling PxCreatePhysics(), and the flag PxVisualDebuggerFlag::eMEMORY when connecting to the debugger.

Note

an important change since 2.x: The SDK now requires that the memory that is returned be 16-byte aligned. On many platforms malloc() returns memory that is 16-byte aligned, but on Windows the system function _aligned_malloc() provides this capability.

Note

On some platforms PhysX uses system library calls to determine the correct type name, and the system function that returns the type name may call the system memory allocator. If you are instrumenting system memory allocations, you may observe this behavior. To prevent PhysX requesting type names, disable allocation names using the method PxFoundation::setReportAllocationNames().

Minimizing dynamic allocation is an important aspect of performance tuning, and PhysX provides several mechanisms to control memory usage.

Reduce allocation used for tracking objects by presizing the capacities of scene data structures, using either PxSceneDesc::limits before creating the scene or the function PxScene::setLimits(). When resizing, the new capacities will be at least as large as required to deal with the objects currently in the scene. These values are only for preallocation and do not represent hard limits, so if you add more objects to the scene than the capacity limits you have set, PhysX will allocate more space.

Much of the memory PhysX uses for simulation is held in a pool of blocks, each 16K in size. You can control the current and maximum size of the pool with the nbContactDataBlocks and maxNbContactDataBlocks members of PxSceneDesc. PhysX will never allocate more than the maximum number of blocks specified, and if there is insufficient memory it will instead simply drop contacts or joint constraints. You can find out how many blocks are currently in use with the getNbContactBlocksUsed() method, and find out the maximum number that have ever been used with the getMaxNbContactDataBlocksUsed() method.

Use PxScene::flush() to reclaim unused blocks, and to shrink the size of scene data structures to the size presently required.

To reduce temporary allocation performed during simulation, provide physx with a memory block in the simulate() call. The block may be reused by the application after the fetchResults() call which marks the end of simulation. The size of the block must be a multiple of 16K, and it must be 16-byte aligned.

You can place PhysX objects in memory owned by the application using PhysX' binary deserialization mechanism. See Serialization for details.

Error Reporting

The error callback PxErrorCallback is a user-defined class which the SDK requires in order to pass error messages to the application. There is only a single function to implement, reportError. This function should log the passed message, or print it on the application's output console. For the more serious error codes eABORT, eINVALID_PARAMETER, eINVALID_OPERATION, and eOUT_OF_MEMORY, breaking into the debugger may be a more appropriate choice. Whatever you do, do not just ignore the messages.

Type Casting

PhysX API interface classes inherit from a top-level interface called PxBase, which provides mechanisms for type-safe down-casting between interface types. For example, to cast from a PxActor to a PxRigidDynamic, use the following idiom:

PxActor* actor = <...>
PxRigidDynamic* myActor = actor->is<PxRigidDynamic>();

const PxActor* actor = <...>
const PxRigidDynamic* myActor = actor->is<PxRigidDynamic>();

This pattern can be used to cast to intermediate types in the hierarchy such as PxRigidActor, but this is somewhat slower than casting to concrete types. In addition, PxBase provides the following capabilities:

  • getConcreteType() provides an integer value which corresponds to the concrete type of an object
  • getConcreteTypeName() provides a string name of the concrete type
  • isKindOf() provides string-based testing of inheritance

Reference Counting

Some PhysX objects are designed to be shared and referenced multiple times in a PhysX scene graph. For example, a PxConvexMesh may be referenced by multiple PxShape objects, each sharing the same geometry but associated with different actors. The specific types are PxTriangleMesh, PxHeightField, PxConvexMesh, PxMaterial, PxClothFabric, and PxShape. Each object of these types has a reference count. The rules for reference counting are as follows:

  • when an object is created from PxPhysics, it has a reference count of 1.
  • when an object's reference count reaches 0, the object is destroyed.
  • when a new counted reference is created, the reference count is incremented. Counted references are as follows:
    • when a PxShape references a PxConvexMesh, PxHeightfield, or PxTriangleMesh.
    • when a PxShape references a PxMaterial.
    • when a PxRigidActor references a PxShape.
    • when a PxCloth references a PxClothFabric.
  • when a counted reference is destroyed, or the object's release() method is called, the reference count is decremented.
  • when an object is created through deserialization, its reference count is 1, plus the number of counted references that exist to the object.

The initial reference count of 1 ensures the object is not destroyed until the application allows it by calling release() - thereafter it will be destroyed when no remaining counted references to it exist.

For example, if you create a shape using PxPhysics::createShape() and attach it to an actor with PxRigidActor::attachShape(), it has a reference count of 2. If you then call the shape's release() method, it has a reference count of 1. When the actor is destroyed, or the shape is detached from the actor, the reference count is decremented, and since it is now 0, the shape is destroyed.

Note

subtypes of PxGeometry do not have counted references to the meshes to which they point, e.g. when PxConvexMeshGeometry points to a PxConvexMesh. A counted reference exists only when the geometry is within a PxShape.

Note

shapes are often created using the utility method PxActor::createShape(). Take special care when deserializing such actors (see Shapes and Geometries and Reference Counting of Deserialized Objects)

Multithreading

PhysX provides efficient multithreaded implementations of its simulation functionality. See Task Management for details of integrating PhysX threading into an application.

For efficiency reasons, PhysX does not internally lock access to its data structures by the application, so be careful when calling the API from multiple application threads. The rules are as follows:

  • API interface methods marked 'const' are read calls, other API interface methods are write calls.
  • API read calls may be made simultaneously from multiple threads.
  • Objects in different scenes may be safely accessed by different threads.
  • Different objects outside a scene may be safely accessed from different threads. Be aware that accessing an object may indirectly cause access to another object via a persistent reference (such as joints and actors referencing one another, an actor referencing a shape, or a shape referencing a mesh.)

Access patterns which do not conform to the above rules may result in data corruption, deadlocks, or crashes. Note in particular that it is not legal to perform a write operation on an object in a scene concurrently with a read operation to an object in the same scene. The checked build contains code which tracks access by application threads to objects within a scene, to try and detect problems at the point when the illegal API call is made.

See Data Access and Buffering for further details of multithreading, including details of writing to a a scene while it is being simulated, and how to coordinate multiple user threads accessing objects which are within a single scene.

Table Of Contents

Previous topic

The Basics

Next topic

Startup and Shutdown