Remove obsolete OVPhysX bootstrap hacks#6074
Conversation
There was a problem hiding this comment.
Summary
This PR removes obsolete compatibility hacks from the OVPhysX integration that were necessary for ovphysx 0.4.x but are no longer needed with ovphysx 0.5's namespaced USD and static Carbonite lifecycle. The changes are well-structured and the test coverage for preventing regression of the removed behavior is thorough.
Positive Observations
-
Clean removal of workarounds: The
pxrmodule hiding during bootstrap, theatexit -> os._exit(0)process-exit workaround, and thephysx.reset()keeping-instance-alive pattern are all cleanly removed. -
Proper lifecycle management: The new
physx.release()call in_release_physx()is cleaner than the previous reset-and-hold pattern. -
Excellent guardrail tests: The new
test_ovphysx_manager_lifecycle.pyexplicitly tests that the old behaviors stay removed, which is great defensive programming. -
Consistent test file updates: All test files consistently update the kitless detection logic and documentation references from
./scripts/run_ovphysx.shto./isaaclab.sh -p -m pytest. -
Clear changelog entries: Both the
.skipand.minor.rstchangelog fragments clearly communicate what changed.
Questions and Suggestions
ovphysx_manager.py
-
Line ~609: The
_construct_physxmethod now unconditionally passesnum_threads=8inPhysXConfig. Was this value validated against ovphysx 0.5's recommended defaults? The old code set this viaset_config_int32orset_settingdynamically. -
Lines ~595-607: The
carbonite_overridesdictionary now includes/physics/physxDispatcher,/physics/updateToUsd, etc. unconditionally. Previously these were set conditionally viaset_setting(). Is there a reason these are now always set even for CPU mode, or is this intentional? -
Potential race condition consideration: With the removal of the
_atexit_registeredguard, if_construct_physxwere somehow called twice (shouldn't happen given the design, but defensively), there's no longer a sentinel preventing duplicate work. The existing_physx is Nonecheck in_warmup_and_loadguards this, but it might be worth adding a comment noting the single-construction guarantee.
test_ovphysx_manager_lifecycle.py
-
Line 82: The
_FakePhysX.release()method setsself.released = True, which is good. Consider adding an assertion thatrelease()wasn't called before (to catch double-release bugs), though this may be out of scope for this PR. -
The fake module fixture restores
_physxand_locked_devicebut not_warmup_done,_stage_path, or other class variables. If any future test accidentally modifies these, it could leak between tests. This is minor since the current tests don't touch them.
pyproject.toml
- Line 26: The version constraint
ovphysx>=0.5,<0.6is good practice. However, consider whether~=0.5(compatible release) would be more appropriate if ovphysx follows semver strictly, though the explicit range is clearer.
Test Interface Files
- The simplification of
_kitlessdetection from the complexLD_PRELOAD-based check to just"EXP_PATH" not in os.environis clean. Just confirming: are there any edge cases whereEXP_PATHmight be set but Kit is still not available? The original check seemed to guard against that.
Minor Nits
- In
contact_sensor.py, the updated error message now reads "The IsaacLab OVPhysX adapter has not wired detailed contact/friction reads" — this is clear but slightly verbose. Consider shortening to "The OVPhysX adapter does not yet support" for consistency with similar messages.
Overall this is a clean, well-tested PR that removes technical debt. The test plan is comprehensive and the changes align well with the ovphysx 0.5 API surface.
Greptile SummaryThis PR removes all OVPhysX compatibility hacks that were required for the pre-0.5 runtime: hiding host
Confidence Score: 4/5Safe to merge; the changes remove well-documented temporary workarounds and the new PhysXConfig construction path is confirmed working by smoke-test runs against the target ovphysx 0.5 wheel. The core manager rewrite is clean and well-tested by both the new lifecycle guardrail tests and the smoke runs. The one thing worth a second look is the new lifecycle test fixture, which only saves and restores _physx and _locked_device while leaving the other class-level fields (_warmup_done, _pending_clones, _usd_handle, etc.) unguarded. This is benign for the three current tests but would silently cause state leakage if future tests exercise a wider slice of the lifecycle through the same fixture. source/isaaclab_ovphysx/test/physics/test_ovphysx_manager_lifecycle.py — the fixture teardown is incomplete relative to the full set of class-level state it could disturb. Important Files Changed
Sequence DiagramsequenceDiagram
participant SC as SimulationContext
participant OM as OvPhysxManager
participant OVP as ovphysx.PhysX
SC->>OM: initialize()
Note over OM: _warmup_done=False, _physx unchanged
SC->>OM: reset()
OM->>OM: _warmup_and_load()
alt _physx is None (first call or after close)
OM->>OVP: bootstrap()
OM->>OVP: "PhysX(device, config=PhysXConfig(...))"
OVP-->>OM: physx instance
Note over OM: _locked_device set
else reuse path (_physx still alive)
OM->>OVP: physx.reset() + wait_op()
end
OM->>OVP: add_usd(stage_file)
OVP-->>OM: usd_handle
OM->>OVP: warmup_gpu() [GPU only]
SC->>OM: step() [repeated]
OM->>OVP: step_sync(dt) + update_articulations_kinematic()
SC->>OM: close()
OM->>OM: _release_physx()
OM->>OVP: physx.release()
Note over OM: _physx=None, _locked_device survives
Reviews (1): Last reviewed commit: "Remove obsolete ovphysx bootstrap hacks" | Re-trigger Greptile |
3ff747a to
d9056d5
Compare
There was a problem hiding this comment.
🤖 Isaac Lab Review Bot — PR #6074
Verdict: Clean removal of technical debt with good guardrail coverage.
Summary
This PR removes OVPhysX 0.4.x compatibility workarounds (pxr-hiding during bootstrap, atexit → os._exit(0) shutdown hack, reset-and-hold instance pattern) and migrates to ovphysx 0.5's PhysXConfig/carbonite_overrides construction API. The changes are well-scoped, the new lifecycle test file prevents regression of the removed workarounds, and test documentation consistently updates references from the deprecated run_ovphysx.sh wrapper to ./isaaclab.sh -p -m pytest.
Findings
🟡 Warning: CI license-check and pre-commit are currently failing
The license-check and pre-commit jobs are failing on this SHA. These may be infrastructure issues, but worth verifying — in particular the new test_ovphysx_manager_lifecycle.py file should pass the license header check and any formatting lints.
🟡 Warning: No error handling around physx.release() in _release_physx
File: source/isaaclab_ovphysx/isaaclab_ovphysx/physics/ovphysx_manager.py
physx = cls._physx
cls._physx = None
physx.release()If physx.release() raises (e.g., the C++ layer encounters an error during teardown), the exception will propagate up through close(). The reference-clearing before release is good defensive practice, but a try/finally or logging around release() would make the failure mode more graceful — especially since this runs during cleanup paths where swallowing or logging errors is often preferable to propagating them.
🟡 Note: _warmup_and_load reuse path narrowed to within-session only
File: ovphysx_manager.py
The reuse path (else branch when cls._physx is not None) in _warmup_and_load calls cls._physx.reset(). Previously this was always reachable because _release_physx kept the instance alive. Now that close() → _release_physx() calls physx.release() and sets _physx = None, the reuse path is only exercised within a single SimulationContext lifetime (i.e., multiple reset() calls before close()). The docstring correctly states "On subsequent calls before close" — just flagging this behavioral narrowing for visibility.
🔵 Suggestion: Document why num_threads=8 is hardcoded
File: ovphysx_manager.py → _construct_physx
num_threads=8 is now a construction-time constant in PhysXConfig. Previously it was set via set_config_int32 or set_setting. If this value is an ovphysx 0.5 API constraint, a brief comment or exposing it as an OvPhysxCfg field would help future maintainers understand the choice and know whether it can be tuned.
🔵 Suggestion: Consider a lifecycle re-construction test
File: test_ovphysx_manager_lifecycle.py
A fourth test verifying _construct_physx succeeds after _release_physx (the close → reinitialize → construct flow) would confirm the new lifecycle's re-entrancy. Optional since real-backend tests cover it, but cheap to add as a guardrail test.
Positive Observations
- ✅ Guardrail tests explicitly verify the old hacks stay removed — excellent defensive testing pattern
- ✅
_kitlessdetection simplification ("EXP_PATH" not in os.environ) is correct and backwards-compatible - ✅ Moving
PhysxManagermock patching inside thetry/except ImportErrorblock in interface tests prevents mock leaks when the backend is not installed - ✅ Clear changelog fragments for both packages (
.skipfor isaaclab,.minor.rstfor isaaclab_ovphysx) - ✅ Version constraint
ovphysx>=0.5,<0.6is appropriately bounded - ✅ The
_release_physxclearing pattern (save ref → null field → release) prevents concurrent access during teardown
Reviewed at SHA: d9056d5
Update (48307ea): Incremental changes reviewed. ✅ Fixed: Test fixture in test_ovphysx_manager_lifecycle.py now properly saves/restores all class-level state fields via _MANAGER_STATE_FIELDS tuple (addressing the earlier inline comment about incomplete fixture state). No new issues introduced in this push — changes are docstring/comment refinements and the fixture improvement. LGTM.
d9056d5 to
48307ea
Compare
|
Stalling this PR for now and marking it as draft. The cleanup intentionally targets the next ovphysx line via We should mark this ready again only after a public Python ovphysx wheel satisfying |
Description
Removes OVPhysX compatibility paths that are no longer needed for the ovphysx 0.5 runtime:
pxrmodules aroundovphysx.bootstrap();atexitprocess-exit shutdown workaround and releases theovphysx.PhysXinstance directly;PhysXConfig/carbonite_overridespath;ovphysx>=0.5,<0.6;Dependency: ovphysx 0.5.
Fixes N/A
Type of change
Screenshots
N/A
Test Plan
./isaaclab.sh -fpython tools/changelog/cli.py check developgit diff --check origin/develop..HEAD && git diff --check2085 passed, 51 skippedpresets=ovphysx, noLD_PRELOAD:Isaac-Cartpole-Direct, 64 envs, 2 RSL-RL iterationsIsaac-Ant-Direct, 16 envs, 2 RSL-RL iterationsIsaac-Humanoid-Direct, 16 envs, 2 RSL-RL iterations16 passed241 passed, 6 skipped, 16 xfailed242 passed, 4 skipped, 18 xfailedChecklist
pre-commitchecks with./isaaclab.sh --formatsource/<pkg>/changelog.d/for every touched package (do not editCHANGELOG.rstor bumpextension.toml— CI handles that)CONTRIBUTORS.mdor my name already exists thereNote: the targeted OVPhysX backend pytest runs still emit existing deprecation/runtime warnings from surrounding test coverage.