[OVPHYSX] RigidObjectCollection asset#5570
Conversation
There was a problem hiding this comment.
Code Review for PR #5570: [OVPHYSX] RigidObjectCollection asset
Thanks for this substantial contribution implementing RigidObjectCollection for the OVPhysX backend! I've thoroughly reviewed the changes and have the following observations:
✅ Strengths
-
Well-designed
_FusedRigidBodyBindingadapter: The approach of wrapping multiple per-bodyRIGID_BODY_*bindings as a single(N, B, D)view is elegant and maintains API consistency with the articulation architecture. -
Comprehensive test coverage: 636 interface tests passing plus 72 backend-specific tests shows good coverage.
-
Proper device handling: The staging buffer management respects CPU vs GPU device boundaries, which is critical for CPU-only quantities like mass and inertia.
-
Good documentation: The docstrings thoroughly explain the architecture decisions (e.g., why articulation bindings can't be used for plain rigid bodies).
🔍 Potential Issues / Questions
1. Memory efficiency in _FusedRigidBodyBinding.write()
In the write method (lines ~148-165 of rigid_object_collection.py), you're using staging[b].assign(wp.from_torch(...)). This creates a temporary warp array from torch on each write. Consider:
# Current (creates intermediate wp.array):
staging[b].assign(wp.from_torch(col, dtype=wp.float32))
binding.write(staging[b], indices=indices, mask=mask)
# Alternative (direct copy into pre-allocated staging):
wp.copy(staging[b], wp.from_torch(col, dtype=wp.float32))However, if wp.from_torch is already zero-copy via DLPack, this may be optimal. Could you confirm the intention here?
2. Potential race condition with _sim_timestamp
In RigidObjectCollectionData.update() (data file ~line 160), the timestamp is incremented:
self._sim_timestamp += dtIf update() is called concurrently with property accessors that check timestamps, there could be a race. This mirrors the existing pattern in other backends, but worth noting if multi-threaded access is ever considered.
3. Cherry-picked commit dependency
The PR notes it carries a cherry-pick from #5545 for ovphysx_manager.py. Please ensure this gets rebased cleanly once #5545 lands to avoid duplicate history.
💡 Suggestions
-
Consider adding a warning for unimplemented material properties: The 4 xfailed tests are for material properties. A clear log message when users try to access these would improve UX:
if tensor_type == TT.RIGID_BODY_MATERIAL: warnings.warn("Material properties not yet supported on OVPhysX backend")
-
Type stub completeness: The
__init__.pyiupdates look good, but consider adding return type hints for the@propertymethods in the data class for better IDE support.
📋 Validation Checklist
- CI passing (labeler ✓)
- Tests: 636 iface + 72 backend-specific passing
- Code follows existing OVPhysX articulation patterns
- Documentation includes architecture rationale
- Changelog fragment present
Verdict
This is solid work that correctly implements the collection abstraction using the binding adapter pattern. The design cleanly handles the OVPhysX constraint that ARTICULATION_LINK_* types don't resolve for plain rigid bodies.
Minor suggestions above are non-blocking. LGTM pending the cherry-pick rebase once #5545 lands.
Greptile SummaryThis PR implements
Confidence Score: 3/5The new RigidObjectCollection is mostly correct, but two mask-based write methods will silently return wrong data in the same sim step they are called, which is exactly the scenario reset-heavy RL environments hit on every episode rollout. The mask-based pose writer and mask-based COM-velocity writer both omit the freshness timestamp assignments their index-based counterparts explicitly set. Any caller writing via the mask API and immediately reading back within the same sim step will receive stale or physics-evolved data instead of the just-written value. Focus on Important Files Changed
Sequence DiagramsequenceDiagram
participant Env as Training Loop
participant ROC as RigidObjectCollection
participant FRB as _FusedRigidBodyBinding
participant OVP as OVPhysX Runtime
participant Data as RigidObjectCollectionData
Env->>ROC: write_body_link_pose_to_sim_index(poses, env_ids, body_ids)
ROC->>ROC: wp.launch(set_body_link_pose_to_sim)
ROC->>Data: "_body_link_pose_w.timestamp = _sim_timestamp"
ROC->>FRB: "binding.write(float32_view, indices=env_ids)"
FRB->>FRB: deassemble (N,B,D) to B x (N,D) staging
FRB->>OVP: per_body[b].write(staging[b], indices)
Env->>ROC: write_data_to_sim()
ROC->>ROC: _body_wrench_to_world kernel
ROC->>FRB: LINK_WRENCH binding.write(wrench_buf)
FRB->>OVP: per_body[b].write(wrench_b)
ROC->>ROC: inst_wrench_composer.reset()
Env->>ROC: update(dt)
ROC->>Data: update(dt) increments _sim_timestamp
Data->>OVP: _read_transform_binding(LINK_POSE) if stale
OVP-->>Data: (N,B,7) poses
Data->>OVP: _read_spatial_vector_binding(LINK_VELOCITY) if stale
OVP-->>Data: (N,B,6) velocities
Reviews (1): Last reviewed commit: "Fix RigidObjectCollection test failures:..." | Re-trigger Greptile |
| binding = self._get_binding(TT.LINK_POSE) | ||
| view = self._make_float32_view(self.data._body_link_pose_w.data, binding) | ||
| binding.write(view, indices=env_ids) | ||
|
|
||
| def write_body_com_pose_to_sim_index( | ||
| self, | ||
| *, | ||
| body_poses: wp.array, | ||
| body_ids: Sequence[int] | wp.array | None = None, |
There was a problem hiding this comment.
Mask-based pose writer does not mark
_body_link_pose_w as fresh
write_body_link_pose_to_sim_index explicitly sets self.data._body_link_pose_w.timestamp = self.data._sim_timestamp (line 459) to prevent the property getter from re-fetching from OVPhysX within the same sim step before update() is called. write_body_link_pose_to_sim_mask omits that assignment entirely, so the next access to body_link_pose_w triggers _read_transform_binding, pulling the physics-evolved pose rather than the just-written value. The corresponding fix (add self.data._body_link_pose_w.timestamp = self.data._sim_timestamp after the kernel launch) is the same pattern used by the index variant and the comment there explains exactly why it is needed.
| binding = self._get_binding(TT.LINK_VELOCITY) | ||
| view = self._make_float32_view(self.data._body_com_vel_w.data, binding) | ||
| binding.write(view, indices=env_ids) | ||
|
|
||
| # ------------------------------------------------------------------ | ||
| # Property setters (3 pairs) | ||
| # ------------------------------------------------------------------ | ||
|
|
||
| def set_masses_index( |
There was a problem hiding this comment.
Mask-based COM velocity writer does not mark
_body_com_vel_w as fresh
write_body_com_velocity_to_sim_index sets self.data._body_com_vel_w.timestamp = self.data._sim_timestamp (line 848) and also invalidates _body_link_vel_w. The mask counterpart sets _body_link_vel_w.timestamp = -1.0 but never advances _body_com_vel_w.timestamp. Consequently, the next call to body_com_vel_w within the same sim step triggers a re-read from OVPhysX, potentially returning the pre-write velocity rather than the value just pushed by the kernel and the binding write.
| # Step 5: Read num_instances from the LINK_POSE fused binding. | ||
| # All fused bindings share the same N (verified implicitly by construction). | ||
| self._num_instances = self._bindings[TT.LINK_POSE].count | ||
|
|
||
| # Step 6: Create the data container. | ||
| self._data = RigidObjectCollectionData( | ||
| root_view=self._bindings, | ||
| num_bodies=self._num_bodies, | ||
| device=self._device, |
There was a problem hiding this comment.
Per-body binding counts are never verified to be equal
N is taken from per_body[0].count but there is no assertion that the remaining B−1 bindings return the same count. If an env-replication mistake or a glob-pattern typo causes one body type to match a different number of prims than the others, the _FusedRigidBodyBinding will have an incorrect shape, producing silently wrong (N, B, D) data and hard-to-diagnose downstream tensor mismatches. A short guard — e.g., asserting all(b.count == N for b in per_body) before constructing the fused adapter — would surface the misconfiguration immediately at initialization.
Verification agents flagged five issues against the previous two commits and one issue-isaac-sim#876 gap that the first pass missed. 1. Newton using-kamino.rst literalinclude path was one level short (../../../../../ → ../../../../../../). The previous depth resolved to a non-existent /docs/source/isaaclab_tasks/... and would have broken the sphinx build. 2. PhysX supported-features.rst listed Ray Caster, Visuo-tactile, and Camera as PhysX-specific sensors. Ray Caster and Camera are implemented in isaaclab core (backend-agnostic); Visuo-tactile lives in isaaclab_contrib. Reframe the section to separate PhysX-implemented sensors from backend-agnostic ones. Drop the unverifiable "path-traced" qualifier on the RTX renderer claim. 3. OvPhysX stub referenced only PR isaac-sim#5426 and PR isaac-sim#5459. The actual in-flight set spans six PRs: isaac-sim#5426 (merged), isaac-sim#5459, isaac-sim#5422, isaac-sim#5421, isaac-sim#5570, isaac-sim#5589. List them all with merge state, and reword "primary covered surfaces" to reflect that most are still open PRs. 4. backends/index.rst feature matrix said OvPhysX sensor coverage was "Partial" — actually only RigidObject is merged. Replace the matrix rows with concrete "In-flight (PR #...)" / "Not yet" cells. 5. Issue isaac-sim#876 asked to "review the limitations list and update it." The previous pass only reworded the intro. Refresh the task list against develop's actual newton_mjwarp coverage, add Shadow Hand / Shadow Hand Over / cabinet / dexsuite / rough-terrain locomotion, and replace the rigid bullet list with a discovery recipe so the list stops bit-rotting.
7a26cfb to
2af401f
Compare
Implements RigidObjectCollection and RigidObjectCollectionData for the OVPhysX backend, completing the rigid-body asset surface alongside RigidObject and Articulation. The collection manages N distinct rigid bodies per environment with (env, body) dual indexing. The asset creates one native fused TensorBinding per tensor type via the ovphysx 0.4.3 create_tensor_binding(prim_paths=...) API, mirroring how PhysX's RigidBodyView aggregates multiple body prims into a single flat view. Each binding spans num_instances * num_bodies prims and returns body-major flat data. Strided-view reshape helpers convert between body- major view layout and the instance-major (N, B, D) layout exposed to users -- no Warp kernels added beyond the resolve_view_ids helper, no per-body Python fan-out at runtime. Also updates the kitless guard in test_rigid_object_collection_iface so the shared interface test can run under run_ovphysx.sh. Fixes isaac-sim#5317
2af401f to
920f2a6
Compare
There was a problem hiding this comment.
Code Review: [OVPHYSX] RigidObjectCollection asset
This PR implements RigidObjectCollection and RigidObjectCollectionData for the OVPhysX backend, completing the rigid-body asset surface alongside RigidObject and Articulation. I've reviewed the implementation across all 10 changed files.
✅ Strengths
-
Efficient fused binding approach: The use of ovphysx 0.4.3's
create_tensor_binding(prim_paths=[...])API creates one native binding per tensor type spanning allnum_instances * num_bodiesprims. This avoids per-body Python fan-out at runtime. -
Well-designed layout conversion: The strided-view reshape helpers (
_reshape_view_to_data_2d/_3dandreshape_data_to_view_2d/_3d) cleanly handle body-major ↔ instance-major layout conversion without requiring Warp kernels. -
Proper dual-path dispatching: The
_binding_writeand_read_binding_into_instance_majormethods correctly dispatch between articulation-mode mock (for iface tests) and native fused binding layouts based onbinding.shape. -
Comprehensive test coverage: The test file covers initialization, state setting, external forces, material properties (xfailed), and state consistency across COM/link frames.
-
Good kitless test guard: The update to
test_rigid_object_collection_iface.pyproperly handles the ovphysx kitless execution path with appropriate module stubbing.
🔍 Observations
1. Timestamp invalidation consistency (minor)
In the pose/velocity write methods, timestamp invalidation is applied to dependent buffers. The pattern is consistent but worth verifying:
write_body_link_pose_to_sim_index: Sets_body_link_pose_w.timestampfresh, invalidates COM-derived bufferswrite_body_com_velocity_to_sim_index: Sets_body_com_vel_w.timestampfresh, invalidates link-derived buffers
This follows the expected dependency graph, but confirm that _body_link_state_w.timestamp and _body_com_state_w.timestamp (deprecated concat properties) are consistently invalidated when their source buffers change.
2. resolve_view_ids kernel placement
The new resolve_view_ids Warp kernel in kernels.py is cleanly isolated. The docstring accurately describes the body-major ordering (view_id = body_id * num_total_envs + env_id).
3. CPU-only binding staging
The CPU staging buffers (_cpu_body_mass, _cpu_body_coms, _cpu_body_inertia) are correctly pinned when the device is GPU. The _get_cpu_env_ids helper efficiently reuses _cpu_all_env_ids for the common all-env case.
💡 Suggestions (non-blocking)
-
Consider explicit material property warning: Since
RIGID_BODY_MATERIALisn't yet exposed by the wheel, consider adding a clear log warning when users attempt operations that would require it, rather than silently failing via the xfailed test gap. -
Docstring for
_TT_MAPconstant: The tensor-type mapping tuple in_initialize_implcould benefit from a brief comment explaining the store-key vs rigid-body-TT relationship.
Verdict
This is a well-structured implementation that correctly extends the OVPhysX backend to support rigid object collections with proper (env, body) dual indexing. The fused binding approach and strided-view reshaping are elegant solutions to the layout differences between backends.
LGTM — pending resolution of the changelog CI failure and rebase once #5545 lands (as noted in the PR description).
📝 Update (920f2a6)
Reviewed SHA changed: 2af401fe → 920f2a62 (force-push/rebase)
✅ Changelog CI now passing — The proper changelog fragment antoiner-feat-ovphysx_rigidobjectcollection.major.rst has been added with a clear feature description.
No concerns from previous review require re-evaluation — the codebase changes appear consistent with the original implementation.
Status: LGTM ✅
…5637) # Description Restructures the physics-backend documentation to give each backend a first-class home and to add the cross-backend orientation pages asked for in [isaac-sim/IsaacLab-Internal#876](isaac-sim/IsaacLab-Internal#876). **Structure** ``` docs/source/overview/core-concepts/physical-backends/ ├── index.rst ← user-facing hub + feature-support matrix ├── solver-comparison.rst ← cross-backend behavioural differences ├── physx/ │ ├── index.rst │ ├── installation.rst │ ├── configuration.rst ← PhysxCfg tuning knobs │ └── supported-features.rst ├── newton/ │ ├── index.rst │ ├── installation.rst │ ├── supported-features.rst (was experimental-features/.../limitations-and-known-bugs.rst) │ ├── mjwarp-solver.rst (was experimental-features/.../solver-transitioning.rst) │ └── kamino-solver.rst (was experimental-features/.../using-kamino.rst) └── ovphysx/ └── index.rst ← stub flagged as highly experimental; tracking issue #5634 ``` **Highlights** - The Newton subdir moves wholesale out of `experimental-features/` via `git mv`; framing changes from "experimental feature branch" to "beta backend." The Experimental Features toctree now contains only `bleeding-edge`. - Per-solver Newton pages (`mjwarp-solver`, `kamino-solver`) replace the old `solver-transitioning` / `using-kamino` files, matching #876's "sub-sections for the Newton solvers" ask. - New PhysX page set written from `isaaclab_physx.physics.PhysxCfg`, mirroring the Newton structure. - OvPhysX stub references the full in-flight PR set (#5421, #5422, #5426, #5459, #5570, #5589) and is gated by a follow-up issue (#5634) for expansion after those land. - `solver-comparison.rst` covers friction, contact pipeline, restitution, stabilization, convergence, articulation coordinates, substepping, and GPU buffers across PhysX TGS, Newton MJWarp, and Newton Kamino. Each cell points at the concrete config attribute that controls the behavior, with a porting checklist at the end. - Newton's `supported-features.rst` list refreshed against `develop`'s actual `newton_mjwarp` coverage (adds Shadow Hand, Shadow Hand Over, cabinet, dexsuite, rough-terrain locomotion). Replaces the bit-rotting bullet list with a discovery recipe (`grep -rln newton_mjwarp source/isaaclab_tasks/`). - Cross-references updated in `docs/index.rst`, `core-concepts/index.rst`, `multi_backend_architecture.rst`, `features/visualization.rst`, and `overview/reinforcement-learning/rl_existing_scripts.rst`. Fixes isaac-sim/IsaacLab-Internal#876 ## Type of change - Documentation update ## Screenshots N/A — docs-only restructure. New page tree visible from the table-of-contents in `core-concepts/`. ## 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 - [ ] I have added tests that prove my fix is effective or that my feature works *(N/A — docs-only)* - [ ] I have added a changelog fragment under `source/<pkg>/changelog.d/` for every touched package *(N/A — docs-only; matches precedent of #5512 and 4aeb4d6 which shipped without fragments)* - [x] I have added my name to the `CONTRIBUTORS.md` or my name already exists there
Description
Implements
RigidObjectCollectionandRigidObjectCollectionDatafor the OVPhysX backend, completing the rigid-body asset surface alongsideRigidObject(#5426) andArticulation(#5459). The collection manages N distinct rigid bodies per environment with(env, body)dual indexing.The asset creates one native fused TensorBinding per tensor type via the ovphysx 0.4.3
create_tensor_binding(prim_paths=[glob_0, …, glob_{B-1}])API, mirroring how PhysX'sRigidBodyViewaggregates multiple body prims into a single flat view. Each binding spansnum_instances * num_bodiesprims and returns body-major flat data(body_0_env_0, body_0_env_1, …, body_1_env_0, …). The data class and asset writers use strided-view reshape helpers (_reshape_view_to_data_2d/_3dandreshape_data_to_view_2d/_3d, ported from the PhysX collection) to convert between body-major view layout and the instance-major(N, B, D)layout exposed to users — no Warp kernels added, no per-body Python fan-out at runtime.Fixes #5317
Stacked on:
ArticulationRigidObjectCarries a one-commit cherry-pick of #5545's
ovphysx_manager.pyportion (required for ovphysx 0.4active_cuda_gpusAPI). Drop the cherry-pick once #5545 lands.Type of change
Screenshots
N/A — backend infrastructure, no visible behaviour change.
Checklist
./isaaclab.sh --formatsource/isaaclab_ovphysx/changelog.d/)CONTRIBUTORS.mdor my name already exists thereTest plan
Tested in a Docker container built from
docker/Dockerfile.base(IsaacSim6.0.0-dev2) with the ovphysx 0.4.3 wheel installed:./scripts/run_ovphysx.sh -m pytest source/isaaclab/test/assets/test_rigid_object_collection_iface.py -k ovphysx→ 636 passed./scripts/run_ovphysx.sh -m pytest source/isaaclab_ovphysx/test/assets/test_rigid_object_collection.py→ 72 passed, 76 skipped (device-mode lock — a second invocation with-k 'cpu'covers CPU), 4 xfailed (material-properties gap shared withRigidObjectuntil the wheel exposesRIGID_BODY_MATERIAL)./isaaclab.sh -f→ clean