Data Access and Buffering

This chapter will discuss read/write access to PhysX objects which are in a scene, specifically the time frame when access is allowed and how/when the changes will take effect. For objects in different scenes or outside a scene, the restrictions are given in the multithreading guidelines in The PhysX API.

General Data Access Rules

It is important to distinguish two time slots for data access:

  1. After the call to PxScene::fetchResults() has returned and before the next PxScene::simulate() call (see figure below, blue area "1").
  2. After the call to PxScene::simulate() has returned and before the corresponding PxScene::fetchResults() call (see figure below, green area "2").

In the first time slot, the simulation is not running and there are no restrictions for reading or writing object properties. Changes to the position of an object, for example, are applied instantaneously and the next scene query or simulation step will take the new state into account.

In the second time slot the simulation is running and in the process, reading and changing the state of objects. Concurrent access from the user might corrupt the state of the objects or lead to data races or inconsistent views in the simulation code. Hence the simulation code's view of the objects is protected from API writes, and any attributes the simulation updates are buffered to allow API reads. The consequences will be discussed in detail in the next section.

Note that simulate() and fetchResults() are write calls on the scene, and as such it is illegal to access any object in the scene while these functions are running.

Double Buffering

While a simulation is running, PhysX supports read and write access to objects in the scene (with some exceptions, see further below). This includes adding/removing them to/from a scene.

From the user perspective, API changes are reflected immediately. For example, if the velocity of a rigid body is set and then queried, the new velocity will be returned. Similarly, if an object is created while the simulation is running, it can be accessed/modified as any other object. However, these changes are buffered so that the simulation code sees the object state as it was when PxScene::simulate() was called. For instance, changes to the filter data of an object while the simulation is running are ignored for collision pair generation of the running step, and will only affect for the next simulation step.

When PxScene::fetchResults() is called, any buffered changes are flushed: changes made by the simulation are reflected in API view of the objects, and API changes are made visible to the simulation code for the next step. User changes take precedence: for example, a user change to the position of an object while the simulation is running will overwrite the position which resulted from the simulation.

The delayed application of updates does not affect scene queries, which always take into account the latest changes.

Events involving removed objects

Deleting objects or removing them from the scene while the simulation is in process will affect the simulation events sent out at PxScene::fetchResults(). The behavior is as follows:

  • PxSimulationEventCallback::onWake(), ::onSleep() events will not get fired if an object is involved which got deleted/removed during the running simulation.
  • PxSimulationEventCallback::onContact(), ::onTrigger() events will get fired if an object is involved which got deleted/removed during the running simulation. The deleted/removed object will be marked as such (see PxContactPairHeaderFlag::eDELETED_ACTOR_0, PxContactPairFlag::eDELETED_SHAPE_0, PxTriggerPairFlag::eDELETED_SHAPE_TRIGGER). Furthermore, if PxPairFlag::eNOTIFY_TOUCH_LOST, ::eNOTIFY_THRESHOLD_FORCE_LOST events were requested for the pair containing the deleted/removed object, then these events will be created.


Not all PhysX objects have full buffering support. Operations which can not run while the simulation is in process are mentioned in the API documentation and the SDK aborts such operations and reports an error. The most important exceptions are as follows:

  • Particles: The particle bulk data can not be read or modified while the simulation is running, this includes operations like reading/writing particle positions/velocities, creating/deleting particles, adding forces, etc.
  • Cloth: The only allowed double buffered operation is to create/delete a cloth and add/remove it to/from the scene.

Memory Considerations

The buffers to store the object changes while the simulation is running are created on demand. If memory usage concerns outweigh the advantage of reading/writing objects in parallel with simulation, do not write to objects while the simulation is running.

Scene Locking

Each PxScene object provides a multiple reader, single writer lock that can be used to control access to the scene by multiple threads. This is useful for situations where the PhysX scene is shared between more than one system, for example APEX and a game engine's physics code. The scene lock provides a way for these systems to coordinate with each other.

The lock is always available and can be made mandatory by setting the PxSceneFlag::eREQUIRE_RW_LOCK flag at scene creation time. If this flag is set a warning will be issued for any API calls made without first acquiring the appropriate lock.

There are four methods for for acquiring / releasing the lock:

void PxScene::lockRead(const char* file=NULL, PxU32 line=0);
void PxScene::unlockRead();

void PxScene::lockWrite(const char* file=NULL, PxU32 line=0);
void PxScene::unlockWrite();

Additionally there is an RAII helper class to manage these locks, see PxSceneLock.h.

Locking Semantics

There are precise rules regarding the usage of the scene lock:

  • Multiple threads may read at the same time.
  • Only one thread may write at a time, no thread may write if any threads are reading.
  • If a thread holds a write lock then it may call both read and write API methods.
  • Re-entrant read locks are supported, meaning a lockRead() on a thread that has already acquired a read lock is permitted. Each lockRead() must have a paired unlockRead().
  • Re-entrant write locks are supported, meaning a lockWrite() on a thread that has already acquired a write lock is permitted. Each lockWrite() must have a paired unlockWrite().
  • Calling lockRead() by a thread that has already acquired the write lock is permitted and the thread will continue to have read and write access. Each lock*() must have an associated unlock*() that occurs in reverse order.
  • Lock upgrading is not supported - a lockWrite() by a thread that has already acquired a read lock is not permitted. Attempting this in checked builds will result in an error, in release builds it will lead to deadlock.
  • Writers are favoured - if a thread attempts a lockWrite() while the read lock is acquired it will be blocked until all readers leave. If new readers arrive while the writer thread is blocked they will be put to sleep and the writer will have first chance to access the scene. This prevents writers being starved in the presence of multiple readers.
  • If multiple writers are queued then the first writer will receive priority, subsequent writers will be granted access according to OS scheduling.

Note: PxScene::release() automatically attempts to acquire the write lock, it is not necessary to acquire it manally before calling release().