Skip to content

Commit 24f2cc0

Browse files
authored
Fix OvPhysX 0.4 compatibility (#5545)
Summary - Fixes OvPhysX backend compatibility with the upcoming ovphysx 0.4 API by using `active_cuda_gpus` and explicit DirectGPU Carbonite settings when supported, while preserving the older `gpu_index` constructor path. - Fixes CPU-only OvPhysX tensor binding reads into GPU-backed articulation buffers. - Uses raw Warp buffers for OvPhysX articulation write views instead of `ProxyArray` wrappers. - Adds the `ovphysx` physics preset to the cartpole camera presets task. Validation - `./isaaclab.sh -f` - `./isaaclab.sh -p -m pytest source/isaaclab_ovphysx/test/assets/test_articulation_data.py source/isaaclab_ovphysx/test/assets/test_articulation.py` - `./isaaclab.sh -p scripts/reinforcement_learning/rsl_rl/train.py --task Isaac-Cartpole-Direct-v0 --num_envs 64 --max_iterations 2 --headless presets=ovphysx` - `./isaaclab.sh -p scripts/reinforcement_learning/rsl_rl/train.py --task Isaac-Ant-Direct-v0 --num_envs 64 --max_iterations 2 --headless presets=ovphysx` - `./isaaclab.sh -p scripts/reinforcement_learning/rsl_rl/train.py --task Isaac-Humanoid-Direct-v0 --num_envs 64 --max_iterations 2 --headless presets=ovphysx` - `./isaaclab.sh -p scripts/reinforcement_learning/rl_games/train.py --task=Isaac-Cartpole-Camera-Presets-Direct-v0 --num_envs=32 --max_iterations=2 --headless --enable_cameras presets=ovphysx,ovrtx_renderer,rgb` # Description This PR fixes several small IsaacLab-side issues needed for the OvPhysX backend to run the supported direct cartpole, ant, and humanoid tasks with the upcoming ovphysx 0.4 wheel. It also enables the cartpole camera presets task to select the `ovphysx` physics preset. The OvPhysX manager now detects the new constructor surface and passes explicit DirectGPU settings for GPU simulations. Older public wheels that still use `gpu_index` keep the previous constructor path. Fixes # (not applicable) ## Type of change - Bug fix (non-breaking change which fixes an issue) - New feature (non-breaking change which adds functionality) ## Screenshots Not applicable. ## Checklist - [x] I have read and understood the [contribution guidelines](https://isaac-sim.github.io/IsaacLab/main/source/refs/contributing.html) - [x] I have run the [`pre-commit` checks](https://pre-commit.com/) with `./isaaclab.sh --format` - [x] I have made corresponding changes to the documentation - [x] My changes generate no new warnings - [x] I have added tests that prove my fix is effective or that my feature works - [x] I have added a changelog fragment under `source/<pkg>/changelog.d/` for every touched package (do **not** edit `CHANGELOG.rst` or bump `extension.toml` — CI handles that) - [x] I have added my name to the `CONTRIBUTORS.md` or my name already exists there
1 parent e15b1d0 commit 24f2cc0

10 files changed

Lines changed: 74 additions & 9 deletions

File tree

CONTRIBUTORS.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -121,6 +121,7 @@ Guidelines for modifications:
121121
* Louis Le Lay
122122
* Lukas Fröhlich
123123
* Manuel Schweiger
124+
* Marco Alesiani
124125
* Masoud Moghani
125126
* Mateo Guaman Castro
126127
* Maurice Rahme
Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
Fixed
2+
^^^^^
3+
4+
* Fixed the sensor prim-deletion callback guard so the OvPhysX backend is not
5+
treated as the Kit PhysX backend.

source/isaaclab/isaaclab/sensors/sensor_base.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -296,7 +296,7 @@ def _invoke(callback_name, event):
296296
)
297297
# Optional: prim deletion (only supported by PhysX backend)
298298
self._prim_deletion_handle = None
299-
if "physx" in physics_mgr_cls.__name__.lower():
299+
if physics_mgr_cls.__name__ == "PhysxManager":
300300
from isaaclab_physx.physics import IsaacEvents # noqa: PLC0415
301301

302302
self._prim_deletion_handle = physics_mgr_cls.register_callback(
Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
Fixed
2+
^^^^^
3+
4+
* Fixed OvPhysX articulation tensor reads and writes for ``ovphysx`` 0.4
5+
compatibility.
6+
* Restored DirectGPU startup settings for OvPhysX GPU simulations.

source/isaaclab_ovphysx/isaaclab_ovphysx/assets/articulation/articulation.py

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1759,7 +1759,7 @@ def _initialize_impl(self) -> None:
17591759
# (keyed on object identity) handles the fast path automatically.
17601760
self._effort_binding = self._get_binding(TT.DOF_ACTUATION_FORCE)
17611761
if self._effort_binding is not None:
1762-
torque = self._data.applied_torque
1762+
torque = self._data._applied_torque
17631763
shape = self._effort_binding.shape
17641764
self._effort_write_view = wp.array(
17651765
ptr=torque.ptr,
@@ -1780,10 +1780,10 @@ def _make_write_view(tt, buf):
17801780
return b, v
17811781

17821782
self._pos_target_binding, self._pos_target_write_view = _make_write_view(
1783-
TT.DOF_POSITION_TARGET, self._data.joint_pos_target
1783+
TT.DOF_POSITION_TARGET, self._data._joint_pos_target
17841784
)
17851785
self._vel_target_binding, self._vel_target_write_view = _make_write_view(
1786-
TT.DOF_VELOCITY_TARGET, self._data.joint_vel_target
1786+
TT.DOF_VELOCITY_TARGET, self._data._joint_vel_target
17871787
)
17881788

17891789
# Let the articulation data know that it is fully instantiated and ready to use.

source/isaaclab_ovphysx/isaaclab_ovphysx/assets/articulation/articulation_data.py

Lines changed: 18 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1684,10 +1684,24 @@ def _read_binding_into_flat(self, tensor_type: int, wp_array: wp.array) -> None:
16841684
16851685
Reads directly into the target array -- no scratch buffer, no extra copy.
16861686
"""
1687+
self._read_binding_into_view(tensor_type, wp_array)
1688+
1689+
def _read_binding_into_view(self, tensor_type: int, view: wp.array) -> None:
1690+
"""Read an ovphysx binding into a float32 warp view."""
16871691
binding = self._get_binding(tensor_type)
16881692
if binding is None:
16891693
return
1690-
binding.read(wp_array)
1694+
1695+
from isaaclab_ovphysx.tensor_types import _CPU_ONLY_TYPES
1696+
1697+
if tensor_type in _CPU_ONLY_TYPES and str(view.device) != "cpu":
1698+
scratch = self._get_read_scratch(tensor_type)
1699+
if scratch is None:
1700+
return
1701+
binding.read(scratch)
1702+
wp.copy(view, scratch)
1703+
else:
1704+
binding.read(view)
16911705

16921706
def _read_binding_into_buf(self, tensor_type: int, buf: TimestampedBuffer) -> None:
16931707
"""Read from an ovphysx binding into a TimestampedBuffer, skipping if fresh."""
@@ -1696,7 +1710,7 @@ def _read_binding_into_buf(self, tensor_type: int, buf: TimestampedBuffer) -> No
16961710
view = self._get_read_view(tensor_type, buf.data)
16971711
if view is None:
16981712
return
1699-
self._get_binding(tensor_type).read(view)
1713+
self._read_binding_into_view(tensor_type, view)
17001714
buf.timestamp = self._sim_timestamp
17011715

17021716
def _read_transform_binding(self, tensor_type: int, buf: TimestampedBuffer) -> None:
@@ -1706,7 +1720,7 @@ def _read_transform_binding(self, tensor_type: int, buf: TimestampedBuffer) -> N
17061720
view = self._get_read_view(tensor_type, buf.data, 7)
17071721
if view is None:
17081722
return
1709-
self._get_binding(tensor_type).read(view)
1723+
self._read_binding_into_view(tensor_type, view)
17101724
buf.timestamp = self._sim_timestamp
17111725

17121726
def _read_spatial_vector_binding(self, tensor_type: int, buf: TimestampedBuffer) -> None:
@@ -1716,7 +1730,7 @@ def _read_spatial_vector_binding(self, tensor_type: int, buf: TimestampedBuffer)
17161730
view = self._get_read_view(tensor_type, buf.data, 6)
17171731
if view is None:
17181732
return
1719-
self._get_binding(tensor_type).read(view)
1733+
self._read_binding_into_view(tensor_type, view)
17201734
buf.timestamp = self._sim_timestamp
17211735

17221736
"""

source/isaaclab_ovphysx/isaaclab_ovphysx/physics/ovphysx_manager.py

Lines changed: 18 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,7 @@
1313
from __future__ import annotations
1414

1515
import atexit
16+
import inspect
1617
import logging
1718
import os
1819
import tempfile
@@ -202,7 +203,23 @@ def _warmup_and_load(cls) -> None:
202203

203204
import ovphysx
204205

205-
cls._physx = ovphysx.PhysX(device=ovphysx_device, gpu_index=gpu_index)
206+
physx_kwargs = {"device": ovphysx_device}
207+
physx_signature = inspect.signature(ovphysx.PhysX)
208+
physx_parameters = physx_signature.parameters
209+
if "active_cuda_gpus" in physx_parameters:
210+
if ovphysx_device == "gpu":
211+
# ovphysx 0.4 accepts a comma-separated CUDA ordinal string; IsaacLab selects one GPU.
212+
physx_kwargs["active_cuda_gpus"] = str(gpu_index)
213+
physx_kwargs["config"] = ovphysx.PhysXConfig(
214+
carbonite_overrides={
215+
"/physics/suppressReadback": True,
216+
"/physics/suppressFabricUpdate": True,
217+
}
218+
)
219+
elif "gpu_index" in physx_parameters:
220+
physx_kwargs["gpu_index"] = gpu_index
221+
222+
cls._physx = ovphysx.PhysX(**physx_kwargs)
206223

207224
# Without worker threads the stepper runs simulate()+fetchResults()
208225
# synchronously, blocking the calling thread for the full GPU step time.

source/isaaclab_ovphysx/test/assets/test_articulation_data.py

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -40,3 +40,19 @@ def test_joint_acc_uses_inverse_dt(self):
4040
atol=1e-6,
4141
err_msg="Joint acceleration should be computed as delta_velocity / dt.",
4242
)
43+
44+
def test_cpu_only_binding_read_stages_to_gpu_view(self):
45+
"""CPU-only bindings should be staged before copying into GPU-backed data buffers."""
46+
if not wp.is_cuda_available():
47+
pytest.skip("CUDA is required to test CPU-to-GPU staging.")
48+
49+
mock_bindings = MockOvPhysxBindingSet(num_instances=1, num_joints=2, num_bodies=1)
50+
data = ArticulationData(mock_bindings.bindings, device="cuda")
51+
data._create_buffers()
52+
53+
expected = np.array([[[1.0, 2.0, 3.0, 0.0, 0.0, 0.0, 1.0]]], dtype=np.float32)
54+
mock_bindings.bindings[TT.BODY_COM_POSE]._data[...] = expected
55+
56+
data._read_transform_binding(TT.BODY_COM_POSE, data._body_com_pose_b)
57+
58+
np.testing.assert_allclose(data._body_com_pose_b.data.numpy(), expected, atol=1e-6)
Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,4 @@
1+
Added
2+
^^^^^
3+
4+
* Added the ``ovphysx`` physics preset to the cartpole camera presets task.

source/isaaclab_tasks/isaaclab_tasks/direct/cartpole/cartpole_camera_presets_env_cfg.py

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@
66
from __future__ import annotations
77

88
from isaaclab_newton.physics import NewtonCfg
9+
from isaaclab_ovphysx.physics import OvPhysxCfg
910
from isaaclab_physx.physics import PhysxCfg
1011

1112
import isaaclab.sim as sim_utils
@@ -27,6 +28,7 @@ class PhysicsCfg(PresetCfg):
2728
default = PhysxCfg()
2829
physx = PhysxCfg()
2930
newton_mjwarp = NewtonCfg()
31+
ovphysx = OvPhysxCfg()
3032

3133

3234
@configclass

0 commit comments

Comments
 (0)