Backend Architecture#
Overview#
Isaac Lab supports multiple physics backends while presenting common asset, sensor, and scene interfaces to environment code. Factories dispatch an object to the active backend implementation at construction time, so code can use the same public API without importing backend-specific modules directly. For choosing a backend or preset in an environment, see Backends and Presets.
Factory dispatch#
All factories inherit from FactoryBase.
They locate supported backend implementations through a core backend-key
selector followed by package and module-path conventions:
The name of
SimulationContext.physics_manageris mapped to one of the backend keys recognized byFactoryBase._get_backend(). Adding another physics backend requires extending this core selector.The factory module path determines the backend module path. For example,
isaaclab.assets.articulationmaps toisaaclab_physx.assets.articulation,isaaclab_newton.assets.articulation, orisaaclab_ov.assets.articulation. The OvPhysX backend key uses the sharedisaaclab_ovintegration package.The factory lazily imports the backend module and caches the implementation class in a registry.
User code: Articulation(cfg)
│
▼
FactoryBase.__new__()
│
├─ _get_backend() → "physx", "newton", or "ovphysx"
│ (reads SimulationContext.physics_manager)
│
├─ _get_module_name() → "isaaclab_physx.assets.articulation"
│ (OvPhysX maps to the shared isaaclab_ov package)
│
├─ importlib.import_module()
│ (lazy load — only on first use)
│
└─ Return backend-specific instance
Some factories use a different resolution key. For example,
Renderer selects an implementation from its
renderer configuration because rendering and physics are independent.
Visualizers similarly use their visualizer_type configuration field.
Physics manager lifecycle#
Each backend implements PhysicsManager, the abstract
base class that owns its simulation lifecycle. Implementations initialize their
engine from a SimulationContext, update kinematics with
forward(), advance simulation with step(), reset state with reset(),
and release resources with close().
The manager exposes PhysicsEvent callbacks for
cross-backend lifecycle work. MODEL_INIT occurs during scene construction,
PHYSICS_READY after physics initialization, and STOP during shutdown.
The concrete close() implementation dispatches the STOP event.
Portable asset and sensor interfaces#
Assets and sensors use the same layering as the factories:
A base class in
isaaclabdefines the public contract, such asBaseArticulationorBaseContactSensor.A factory class inherits from both
FactoryBaseand that base class.Backend packages provide the supported implementations.
Data classes use the same pattern, for example
ArticulationData(FactoryBase, BaseArticulationData). Implementations expose
ProxyArray values through public asset and sensor
data properties. Each proxy wraps the underlying wp.array and provides
explicit .warp access to that array and cached, zero-copy .torch access
to a torch.Tensor view. Use those accessors when an API specifically
requires one representation. Passing a ProxyArray to wp.to_torch() is
supported only by a deprecated compatibility shim; new code should use
proxy_array.torch. Backend-native and internal storage may still use raw
Warp arrays. See Working with ProxyArray for usage and buffer lifetime
guidance.
Portable renderer and scene-data interfaces#
Rendering is selected independently from physics. Renderer configurations
dispatch through Renderer to implementations that
share the BaseRenderer contract, with
RenderContext owning their lifecycle. See
Renderers for renderer choices and usage.
Physics managers expose live simulation data through
SceneDataBackend. The
SceneDataProvider owned by the simulation context
converts and remaps that data for backend-independent consumers:
physics manager -> SceneDataBackend -> SceneDataProvider -> renderer or visualizer
This boundary lets renderers and visualizers consume a common Warp-native data path without knowing which physics engine owns the state. See Scene Data Provider for the complete data-flow model.
Native engine access boundary#
The portable interfaces define the stable API boundary. Advanced code can use each engine’s native low-level data API, but those APIs intentionally keep their own ownership and synchronization semantics. See Native Physics API Access for PhysX typed views, Newton live model/state arrays and generic selections, and OvPhysX tensor bindings.
Design principles#
Lazy loading: Backend modules are imported only when first instantiated, keeping startup fast and avoiding dependencies on unused backends.
Recognized keys plus convention: Once the core selector recognizes a backend key, module paths mirror the
isaaclab.X.Ystructure. OvPhysX maps toisaaclab_ov.X.Y; other recognized backends useisaaclab_<backend>.X.Yby default.Independent selection: Physics backend, renderer, and visualizer are selected independently.
Explicit data interop: Public asset and sensor data properties return
ProxyArray; its.warpand.torchaccessors expose the required array representation without copying.Zero runtime overhead: Selection occurs at instantiation time; it does not add dispatch logic to the simulation hot path.