Simulation Performance Guide#

Best practices for optimizing simulation performance with ovphysx. Most tuning is either a per-scene USD attribute on the codeless PhysxSceneAPI (refer to Physics Scene and Physics Schemas) or a PhysXConfig option on the instance.

Solver Iterations#

Lowering solver iterations to the smallest count that still gives acceptable fidelity helps a lot — clamp the per-scene iteration counts. PGS is typically faster per iteration; TGS often reaches better stability with fewer iterations. For the attributes and how to author them, refer to Physics Solver.

CPU vs GPU by Scene Size#

For only a few rigid bodies and articulations, CPU simulation is often faster; for large scenes (for example RL with many environments), use GPU simulation and access state through the batch tensor API. How to select CPU vs GPU (per-scene physxScene:enableGPUDynamics, process-wide set_cpu_mode, and GPU selection) is covered in CPU vs GPU Simulation.

Reading State Efficiently#

Unlike Kit-based Omni PhysX, ovphysx does not write simulation output back to USD, so there is no per-step USD write-back cost. Read state in bulk through tensor bindings (DLPack, zero-copy on device) or the ovstage output read API rather than per-prim.

Physics Thread Count#

Adjust the number of CPU simulation threads. For small scenes, single-threaded (num_threads=0) often gives the best performance:

from ovphysx import PhysX, PhysXConfig
physx = PhysX(config=PhysXConfig(num_threads=0))

Stepping Cadence#

ovphysx steps explicitly through step() / step_sync(), so you control how often physics runs relative to any rendering or control loop — there is no Kit-style asynchronous scene-update mode to configure. Keep any per-step Python work light, since it runs inline with your loop and can become the bottleneck.

Avoid Unintended Collision Overlap in RL Scenes#

When building many parallel environments, do not duplicate infinite collision geometry such as ground planes. A ground plane is infinite, so each duplicate touches every environment, producing large numbers of expensive inter-environment overlaps. Author one shared ground, or use finite ground colliders positioned per environment. OmniPVD recording is useful to inspect the scene and confirm there are no unexpected overlaps.

Disable Scene-Query Support#

If you do not use scene queries (raycast/sweep/overlap), disable support to save performance:

def PhysicsScene "physicsScene" (
    prepend apiSchemas = ["PhysxSceneAPI"]
)
{
    bool physxScene:enableSceneQuerySupport = false
}

Multi-GPU#

On multi-GPU machines you can distribute simulation across GPUs (through active_cuda_gpus and PhysXConfig(scene_multi_gpu_mode=...)) for a potential speedup — refer to CPU vs GPU Simulation.

Collision Geometry Choice#

Simpler collision geometry is faster: an SDF mesh collider is far more expensive than a sphere. Prefer primitives, then convex hulls, before triangle/SDF meshes (refer to Colliders).

In GPU RL workloads, watch for warnings that a convexHull mesh failed to cook a GPU-compatible hull (common for long, thin, high-aspect-ratio inputs). Such a mesh falls back to CPU contact generation, which can badly hurt performance and prevents interaction with GPU-only features. Workarounds: use a bounding-cube approximation, or a static triangle mesh if the geometry is not on a dynamic body.

Deformables and Particles#

Deformables and particles are considerably more expensive than rigid bodies. Where feasible, approximate a deformable with rigid bodies plus compliant contacts, or rigid bodies connected by joints.