Skip to content

[Core] Make tests backend-agnostic and start them with launch_test_simulation (3/3) - #8102

Merged
ooctipus merged 2 commits into
isaac-sim:developfrom
ooctipus:octi/launch-simulation-tests
Sep 28, 2026
Merged

ooctipus merged 2 commits into
isaac-sim:developfrom
ooctipus:octi/launch-simulation-tests

Conversation

@ooctipus

Copy link
Copy Markdown
Collaborator

Description

Part 3 of 3. #8068 (merged) added the launcher design, and #8090 moves the scripts onto it. This PR does the same for the tests. It is independent of #8090, so they can merge in either order.

What changes

  • One-line launch: test modules start the runtime with a single launch_test_simulation(...) call. It replaces the """Launch Isaac Sim Simulator first.""" preamble in about 125 modules.
  • launch_test_simulation(cfg=None, **launcher_args) is config-driven. It starts whatever runtime the test's simulation config needs (the default SimulationCfg() when none is given) instead of forcing Kit, and it returns None. It still picks the test device from the multi-GPU mask by default. Every existing test gets the same runtime as before, because the default config uses PhysX, which starts Kit; the config makes that choice now, not the helper.
  • Core isaaclab tests no longer import omni, carb, or isaacsim:
    • camera tests rely on the simulation's own cleanup instead of Replicator's destroy_hydra_textures;
    • tests that check Kit, PhysX, or Isaac Sim behavior move to isaaclab_physx: the Isaac Sim XformPrim comparison, the Kit timeline and app-update tests, stage-in-memory, the UI window, Kit startup performance, and three Isaac Sim extension-path tests. No coverage is deleted.
  • Tests that need the Kit app object get it from omni.kit.app in their own package: Newton fabric sync, teleop XR, and the visualizer tests.

The remaining Kit mentions in core tests are the isaacsim_ci CI markers, the install_ci installer checks, omni.client, and tests that stub omni to exercise core code's own Kit branches without starting Kit.

Net: +856 / −1,260 across 169 files.

Type of change

  • Breaking change (launch_test_simulation signature and return value)

Validation

All runs on the display GPU (GPU 0).

  • Every changed or moved module passes, including the app tests (43), simulation context (26), the moved Kit context tests (4), camera (21), Newton fabric sync (9), views, controllers, the other camera tests, pink IK, and teleop XR.
  • Visualizer tests: the integration and golden-image tests fail on this machine exactly as they do without this PR (a known flat-frame issue); CI is the check for those.

Release backport

  • Backport this pull request to the active release branch after it merges into develop

Checklist

  • I have read and understood the contribution guidelines
  • I have run the pre-commit checks
  • My changes generate no new warnings
  • I have added changelog fragments for every touched package

Test modules start the runtime with a single launch_test_simulation(...) call,
which now takes an optional config and starts the runtime that config needs
instead of forcing Kit. Core isaaclab tests no longer import omni, carb, or
isaacsim; tests of Kit, PhysX, or Isaac Sim behavior move to isaaclab_physx.
@ooctipus

Copy link
Copy Markdown
Collaborator Author

run-ci

@github-actions github-actions Bot added asset New asset feature or request isaac-mimic Related to Isaac Mimic team labels Sep 28, 2026
@isaaclab-bot isaaclab-bot Bot added ci:run-docker Trigger the on-demand Docker and GPU CI workflow and removed ci:run-docker Trigger the on-demand Docker and GPU CI workflow labels Sep 28, 2026
@greptile-apps

greptile-apps Bot commented Sep 28, 2026 •

Copy link
Copy Markdown
Contributor

RetriggerConfidence Score: 4/5

[Medium risk] Refactors test startup to use a new simulation launcher function.

The PR appears safe to merge, with non-blocking test coverage needed for its new config-driven launch path.

Findings

  1. P2 Explicit Config Path Untested ▶

Summary

The PR migrates test modules to launch_test_simulation, makes that helper accept a runtime-selecting config, and relocates Kit-specific tests to isaaclab_physx. The new explicit-config path lacks direct test coverage.

Diagram

%%{init: {'theme': 'neutral'}}%%
flowchart LR
  T[Test module] --> H[launch_test_simulation]
  H --> C{Config supplied?}
  C -- No --> D[Default SimulationCfg]
  C -- Yes --> E[Supplied config]
  D --> L[launch_simulation]
  E --> L
  L --> R[Selected runtime]
Loading

Reviews (1) · Last reviewed commit: "Make tests backend-agnostic and start th..."

Comment on lines +34 to +35
cfg = SimulationCfg() if cfg is None else cfg
_RUNTIME.enter_context(launch_simulation(cfg, {"headless": True, **launcher_args}))

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 Explicit config path untested. The new cfg parameter lets tests select a runtime, but the migrated tests all call this helper without a config. They exercise only the default Kit path, so a regression that starts Kit for a Newton or other kitless config could go unnoticed. A focused test with an explicit non-Kit config would cover the new behavior.

Note: If this suggestion doesn't match your team's coding style, reply to this and let me know. I'll remember it for next time!

@isaaclab-review-bot isaaclab-review-bot Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Isaac Lab Review Bot

The backend-agnostic test-launch migration and relocation of Kit/PhysX-specific coverage are coherent. One API maintainability fix remains: annotate the newly added public cfg parameter on launch_test_simulation.

  • Design and architecture: The shared launcher remains in isaaclab.test.utils, while Kit-, PhysX-, and Isaac Sim-specific tests move into isaaclab_physx, preserving the intended dependency direction. Deferred runtime imports also retain the pre-launch import boundary.
  • API: The return-value and configuration-driven behavior changes are documented with migration guidance, and in-repository callers were updated. However, the new public cfg parameter is untyped; add a concrete SimulationCfg | None annotation using a deferred or TYPE_CHECKING import.
  • Implementation: The migrated callers preserve relevant launcher arguments, and tests requiring direct Kit app access now obtain it explicitly. No additional material implementation issue from the candidate findings is supported by the patch.

Minor fixes needed. Posted 1 actionable finding inline.

Automated review; human maintainers own approval decisions.


def launch_test_simulation(**launcher_args):
"""Start Isaac Sim / Kit for the rest of the test process through :func:`~isaaclab.app.launch_simulation`.
def launch_test_simulation(cfg=None, **launcher_args) -> None:

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🔵 Suggestion · Api — Annotate the new public cfg parameter

launch_test_simulation is referenced as a public helper in the changelog and its return is annotated, but the new first positional parameter cfg has no type. The module already uses from __future__ import annotations, so cfg: SimulationCfg | None = None with a TYPE_CHECKING import documents the contract while keeping the runtime import deferred.

The Kit-visualizer paths in visualizer_integration_utils update the running Kit
app directly, so the five visualizer test modules no longer import omni.kit.app
or register an app object.
@ooctipus

Copy link
Copy Markdown
Collaborator Author

run-ci

@isaaclab-bot isaaclab-bot Bot added ci:run-docker Trigger the on-demand Docker and GPU CI workflow and removed ci:run-docker Trigger the on-demand Docker and GPU CI workflow labels Sep 28, 2026
@ooctipus

Copy link
Copy Markdown
Collaborator Author

run-ci

@isaaclab-bot isaaclab-bot Bot added ci:run-docker Trigger the on-demand Docker and GPU CI workflow and removed ci:run-docker Trigger the on-demand Docker and GPU CI workflow labels Sep 28, 2026
@ooctipus
ooctipus merged commit c7775bc into isaac-sim:develop Sep 28, 2026
53 checks passed
isaaclab-bot Bot pushed a commit that referenced this pull request Sep 28, 2026
…mulation (3/3) (#8102)

## Description

**Part 3 of 3.** #8068 (merged) added the launcher design, and #8090
moves the scripts onto it. This PR does the same for the tests. It is
independent of #8090, so they can merge in either order.

### What changes

- **One-line launch:** test modules start the runtime with a single
`launch_test_simulation(...)` call. It replaces the `"""Launch Isaac Sim
Simulator first."""` preamble in about 125 modules.
- **`launch_test_simulation(cfg=None, **launcher_args)` is
config-driven.** It starts whatever runtime the test's simulation config
needs (the default `SimulationCfg()` when none is given) instead of
forcing Kit, and it returns `None`. It still picks the test device from
the multi-GPU mask by default. Every existing test gets the same runtime
as before, because the default config uses PhysX, which starts Kit; the
config makes that choice now, not the helper.
- **Core `isaaclab` tests no longer import `omni`, `carb`, or
`isaacsim`:**
- camera tests rely on the simulation's own cleanup instead of
Replicator's `destroy_hydra_textures`;
- tests that check Kit, PhysX, or Isaac Sim behavior move to
`isaaclab_physx`: the Isaac Sim `XformPrim` comparison, the Kit timeline
and app-update tests, stage-in-memory, the UI window, Kit startup
performance, and three Isaac Sim extension-path tests. No coverage is
deleted.
- **Tests that need the Kit app object** get it from `omni.kit.app` in
their own package: Newton fabric sync, teleop XR, and the visualizer
tests.

The remaining Kit mentions in core tests are the `isaacsim_ci` CI
markers, the `install_ci` installer checks, `omni.client`, and tests
that stub `omni` to exercise core code's own Kit branches without
starting Kit.

Net: +856 / −1,260 across 169 files.

## Type of change

- Breaking change (`launch_test_simulation` signature and return value)

## Validation

All runs on the display GPU (GPU 0).

- **Every changed or moved module passes,** including the app tests
(43), simulation context (26), the moved Kit context tests (4), camera
(21), Newton fabric sync (9), views, controllers, the other camera
tests, pink IK, and teleop XR.
- **Visualizer tests:** the integration and golden-image tests fail on
this machine exactly as they do without this PR (a known flat-frame
issue); CI is the check for those.

## Release backport

- [x] <!-- backport-active-release --> Backport this pull request to the
active release branch after it merges into `develop`

## Checklist

- [x] I have read and understood the contribution guidelines
- [x] I have run the pre-commit checks
- [x] My changes generate no new warnings
- [x] I have added changelog fragments for every touched package

(cherry picked from commit c7775bc)
@isaaclab-bot

isaaclab-bot Bot commented Sep 28, 2026

Copy link
Copy Markdown
Contributor

Backported to release/3.0.0 as 64586a8.

ooctipus added a commit that referenced this pull request Sep 28, 2026
…8090)

## Description

**Part 2 of 3.** #8068 (merged) added the launcher design; this PR moves
every script, example, tutorial, benchmark, and doc page onto it. The
tests are in #8102. The two PRs are independent and can merge in either
order.

```python
from isaaclab.app import add_launcher_args, launch_simulation
from isaaclab.utils import instantiate

add_launcher_args(parser)
args_cli = parser.parse_args()
...
with launch_simulation(env_cfg, args_cli):   # the runtime follows the config
    env = instantiate(env_cfg)
```

### What changes

- **Every script, example, tutorial, and benchmark** starts the runtime
with `launch_simulation` and builds runtime objects with `instantiate`
(#8085). Nothing outside the shim below uses `AppLauncher` any more.
- **Kit calls leave the scripts:**
- the teleop, record, and replay scripts no longer load Replicator or
update the app after closing the env;
- `pick_and_place.py` uses `Se3Keyboard` instead of raw `carb.input`
(W/S/A/D move, Q/E height, K gripper, N/M aim). On develop it died
before stepping;
- the camera tutorials save PNGs with `save_images_to_file` instead of
Replicator writers.
- **The converter tools** (`convert_mesh`, `convert_urdf`,
`convert_mjcf`) preview the converted asset in the visualizer chosen
with `--viz`, Kit included, instead of a Kit-only viewport.
`isaaclab_physx.app.show_stage_in_viewport` has no callers left and is
removed.
- **The 14 `check_*` scripts** under the core and PhysX test folders,
which duplicated pytest coverage, are deleted.
- **`AppLauncher` shrinks to an init-only shim**, so scripts written
against the old API keep running:
- `AppLauncher.add_app_launcher_args(parser)` adds the launcher
arguments;
- `AppLauncher(args).app` starts Kit through `launch_simulation`, and
`app.close()` stops it;
  - `is_available`, `has_gui`, `has_window`, and `device` are removed.

The runtime is held for the process, so a script that drops the launcher
keeps Kit running. `test_app_launcher_shim.py` covers both a kept and a
dropped launcher; the dropped case fails without the fix.
- **Moved with their scripts:** the two converter tests and the
replay-loop test, which read script code, and `wait_for_stage_load`,
used by the teleop replay script.

Net: +2,367 / −4,334 across 121 files.

## Type of change

- Breaking change (`AppLauncher` reduced to a shim)

## Validation

All runs on the display GPU (GPU 0).

- **Every script under `scripts/` and `examples/`** was run on develop
and with these changes, using the same invocation. No script that runs
on develop fails here.
- Of the 78 scripts outside the standalone matrix, 52 run, 19 are
blocked by the same missing resource on both (datasets, XR, a Ray
cluster, gr00t, robomimic, moviepy), and 7 fail the same way on both.
  - The standalone matrix (361 cases) shows no new failures.
- **Converter tools:** each writes its USD. `--viz kit` and `--viz
newton_gl` keep rendering the asset until the window closes.
- **Tests:** the shim test (2), URDF converter (17), replay loop (1),
and RTX utils (10) pass.
- **Training:** Cartpole on `isaacsim_physx` runs 2 iterations.

## Release backport

- [x] <!-- backport-active-release --> Backport this pull request to the
active release branch after it merges into `develop`

## Checklist

- [x] I have read and understood the contribution guidelines
- [x] I have run the pre-commit checks
- [x] I have made corresponding changes to the documentation (tutorials
and how-to guides)
- [x] My changes generate no new warnings
- [x] I have added changelog fragments for every touched package
isaaclab-bot Bot pushed a commit that referenced this pull request Sep 28, 2026
…8090)

## Description

**Part 2 of 3.** #8068 (merged) added the launcher design; this PR moves
every script, example, tutorial, benchmark, and doc page onto it. The
tests are in #8102. The two PRs are independent and can merge in either
order.

```python
from isaaclab.app import add_launcher_args, launch_simulation
from isaaclab.utils import instantiate

add_launcher_args(parser)
args_cli = parser.parse_args()
...
with launch_simulation(env_cfg, args_cli):   # the runtime follows the config
    env = instantiate(env_cfg)
```

### What changes

- **Every script, example, tutorial, and benchmark** starts the runtime
with `launch_simulation` and builds runtime objects with `instantiate`
(#8085). Nothing outside the shim below uses `AppLauncher` any more.
- **Kit calls leave the scripts:**
- the teleop, record, and replay scripts no longer load Replicator or
update the app after closing the env;
- `pick_and_place.py` uses `Se3Keyboard` instead of raw `carb.input`
(W/S/A/D move, Q/E height, K gripper, N/M aim). On develop it died
before stepping;
- the camera tutorials save PNGs with `save_images_to_file` instead of
Replicator writers.
- **The converter tools** (`convert_mesh`, `convert_urdf`,
`convert_mjcf`) preview the converted asset in the visualizer chosen
with `--viz`, Kit included, instead of a Kit-only viewport.
`isaaclab_physx.app.show_stage_in_viewport` has no callers left and is
removed.
- **The 14 `check_*` scripts** under the core and PhysX test folders,
which duplicated pytest coverage, are deleted.
- **`AppLauncher` shrinks to an init-only shim**, so scripts written
against the old API keep running:
- `AppLauncher.add_app_launcher_args(parser)` adds the launcher
arguments;
- `AppLauncher(args).app` starts Kit through `launch_simulation`, and
`app.close()` stops it;
  - `is_available`, `has_gui`, `has_window`, and `device` are removed.

The runtime is held for the process, so a script that drops the launcher
keeps Kit running. `test_app_launcher_shim.py` covers both a kept and a
dropped launcher; the dropped case fails without the fix.
- **Moved with their scripts:** the two converter tests and the
replay-loop test, which read script code, and `wait_for_stage_load`,
used by the teleop replay script.

Net: +2,367 / −4,334 across 121 files.

## Type of change

- Breaking change (`AppLauncher` reduced to a shim)

## Validation

All runs on the display GPU (GPU 0).

- **Every script under `scripts/` and `examples/`** was run on develop
and with these changes, using the same invocation. No script that runs
on develop fails here.
- Of the 78 scripts outside the standalone matrix, 52 run, 19 are
blocked by the same missing resource on both (datasets, XR, a Ray
cluster, gr00t, robomimic, moviepy), and 7 fail the same way on both.
  - The standalone matrix (361 cases) shows no new failures.
- **Converter tools:** each writes its USD. `--viz kit` and `--viz
newton_gl` keep rendering the asset until the window closes.
- **Tests:** the shim test (2), URDF converter (17), replay loop (1),
and RTX utils (10) pass.
- **Training:** Cartpole on `isaacsim_physx` runs 2 iterations.

## Release backport

- [x] <!-- backport-active-release --> Backport this pull request to the
active release branch after it merges into `develop`

## Checklist

- [x] I have read and understood the contribution guidelines
- [x] I have run the pre-commit checks
- [x] I have made corresponding changes to the documentation (tutorials
and how-to guides)
- [x] My changes generate no new warnings
- [x] I have added changelog fragments for every touched package

(cherry picked from commit 443dc47)
StafaH added a commit that referenced this pull request Sep 28, 2026
# Description

Part 14 of the split of #7949. This PR cleans up sim authoring: the
schema writers in `isaaclab.sim.schemas`, the spawners, and the mesh
converter. It is rebased onto the current `develop` and ports only the
parts of #7949 that still apply there. The authored USD is unchanged,
and no public API changes.

**Spawners and mesh converter**
- `spawners.utils` (renamed from `spawners._utils`, see below) adds a
helper:
- `apply_schema_props` writes a spawner schema slot. It sends a fragment
mapping to the fragment writer, one entry at a time and anchored at the
spawned prim, and sends a legacy cfg to the legacy writer. The mesh and
shape spawners, `spawn_from_mesh`, and `MeshConverter` repeated this
branch for their collision, mass, and rigid-body slots; they now call
the helper.
- Material paths are resolved inline at each call site (absolute paths
are kept; relative names go under the spawned prim), replacing
`from_files._resolve_material_path`.
- Existing-prim checks are left to `create_prim`, which already raises
the same `ValueError`. The spawner bodies are shorter, and
`spawn_geom_from_prim_type` / `_spawn_mesh_geom_from_mesh` return the
prim they create.
- Mesh spawners share `_axis_transform` for the cylinder, capsule, and
cone axis rotation.
- Light, sensor, ground plane, and multi-USD spawners:
  - The ground plane hides its light with `set_prim_visibility`.
- `spawn_multi_usd_file` builds `MultiAssetSpawnerCfg` in one call and
no longer runs a `hasattr` check that was always true.
- `AssetConverterBase` has a shorter USD-extension and hash-file check,
and writes the `config.yaml` generation stamp with a single
`datetime.now()` call. The stamp format is the same.

**Schema writers**
- `apply_rigid_body_properties`, `apply_collision_properties`, and
`apply_mass_properties` now share `_apply_api_fragments`. Target
matching, `create_if_missing`, the zero-target warning, and result
aggregation are unchanged.
- The joint-drive writers share `_write_drive_attributes`, which
converts angular stiffness and damping from per-radian to per-degree and
maps `drive_type` to the USD `type` attribute. It is used by
`apply_drive`, `_ensure_drive_exists`, `apply_joint_drive_properties`,
and `modify_joint_drive_properties`. Joint-type detection reuses
`drive_instance_name`, including in `schemas_actuators`.
- Other shared helpers:
- `_write_mesh_approximation` writes and validates the
`physics:approximation` token.
- `_tetrahedralize_surface` holds the pytetwild call and the tet-winding
fix, moved out of `define_deformable_body_properties`.
- Smaller cleanups:
  - `activate_contact_sensors` counts descendants with `Usd.PrimRange`.
- `schemas_actuators` uses `to_camel_case` instead of a private
camel-case regex.
- Redundant `pass` statements are removed from the fragment marker
classes.
- Fragment appliers are called directly as `cfg.func(cfg, prim_path,
stage)`. `@configclass` already turns the `func` import string into a
callable `ResolvableString`, so the `callable(...) else
string_to_callable(...)` fallback (four schema writers and the
physics-material spawner) never ran.

**Reflection**
- Replaced avoidable `getattr`/`setattr`/`hasattr` with direct attribute
access or an `isinstance` check wherever the attribute is always defined
for the configs that reach that code (actuator authoring, the USD-file
and shape spawners, the camera spawner's intrinsics, the fragment
annotation lookup). The remaining uses are dynamic by design: iterating
dataclass fields, the deprecated alias forwarding, optional class
metadata, and mixed config types in the multi-asset wrapper.

**No underscore module names or cross-file private helpers in core**
- Renamed the underscore modules: `sim/spawners/_utils.py` → `utils.py`,
`sim/schemas/_backend_hooks.py` → `backend_hooks.py`,
`actuators/_compat.py` → `actuator_compat.py`, `benchmark/_cli.py` →
`cli.py`, `cloner/_fabric_notices.py` → `fabric_notices.py`,
`utils/_device.py` → `device.py`. All importers in `isaaclab`,
`isaaclab_physx`, `isaaclab_newton`, `isaaclab_ov`, the benchmark
scripts, tests, and docs are updated.
- Dropped the `_` prefix from module-level functions used from other
files: `limits_equal`, `resolve_limit_aliases`, `skip_joint_drive`,
`articulation_root_companion_namespace`, `apply_namespaced_schemas`,
`drive_instance_name`, `validate_newton_native_actuator_cfgs`,
`author_actuator_prims`, `resave_checkpoint_with_metadata`,
`deprecate_field_alias`, `deprecated_schema_cfg`, `spawn_from_usd_file`,
`spawn_geom_from_prim_type`. They are not added to the package exports.

**Not ported from #7949**
- The forwarding-shim refactors in `schemas/__init__.py` and
`schemas_cfg.py`, the `usd_api` / `physx_api` deprecation properties,
and the empty `_usd_*` metadata on deprecated cfgs. These are
deprecation-only and will be removed.
- The `_OVERRIDABLE_PROPS` constant in the wrappers, a single-use
constant.
- The import-only edits. `develop` already uses relative imports.

## Type of change

- Refactor (no functional change). The only visible difference is that
the `ValueError` for an unsupported deformable type is now one line,
instead of a triple-quoted string with embedded indentation.

## Verification

- USD authoring equivalence, comparing `develop` sources with this
branch in Kit, one fresh stage per case. All 57 cases produce identical
exported USDA, return values, and raised errors:
- Sphere, cuboid, cylinder, capsule, and cone shapes; and sphere,
cuboid, cylinder, capsule, cone, and rectangle meshes. Each is spawned
plain, with legacy cfgs, and with fragments (including dict targets and
absolute material paths).
- The cable spawner, `MeshFileCfg` from a trimesh object and from an
`.obj` file, and all five lights.
  - Pinhole and fisheye cameras, and the sensor frame.
- `UsdFileCfg` with legacy cfgs and with fragments (articulation root,
joint drives, contact sensors, semantics, materials), the
compliant-contact spawner, and a ground plane asset.
- `MultiAssetSpawnerCfg` and `MultiUsdFileCfg`, and a surface
deformable.
- The legacy `define_`/`modify_` writers and the fragment `apply_*`
writers, including `ensure_drives_exist`, the no-target case, mesh
collision with an invalid approximation name, and fixed tendons.
- A `MeshConverter` run with collision, mesh-collision, mass, and rigid
props and `make_instanceable`, comparing the generated USD layers and
`config.yaml`.
- Tests, run one file at a time; all pass:
- Spawners: `test_spawn_shapes`, `test_spawn_meshes`,
`test_spawn_lights`, `test_spawn_sensors`, `test_spawn_from_files`,
`test_spawn_wrappers`, `test_spawn_cable`, `test_spawn_materials`,
`test_default_ground_plane`, `test_visual_materials_kitless`.
- Schemas: `test_schemas`, `test_schemas_deprecation`,
`test_schemas_shim`, `test_schemas_kitless_fixed_joint`,
`test_schema_fragments`, `test_schema_fragment_parity`,
`test_schema_writer_nested_targets`, `test_joint_drive_fragments`,
`test_collision_fragments`, `test_mass_fragments`,
`test_mesh_collision_fragments`, `test_articulation_fragments`,
`test_material_fragments`, `test_tendon_fragments`,
`test_tendon_fragments_codeless`, `test_deformable_backend_split`.
  - Converter: `test_mesh_converter`.
- Actuator authoring: `source/isaaclab/test/actuators`,
`test_newton_actuator_manager_authoring`,
`test_newton_actuators_newton`, `test_newton_actuators_physx`.
- After the renames, reflection pass, and review fixes, the 57-case USD
comparison was rerun against the latest `develop` (0 differences), and
the spawner, schema, fragment, converter, cloner (`isaaclab` and
`isaaclab_physx`), benchmark CLI, device, Newton schema, and actuator
authoring tests were rerun; all pass. Every renamed module and its
importers in `isaaclab_physx`, `isaaclab_newton`, and `isaaclab_ov`
import cleanly.
- One-line helpers are kept inline rather than wrapped in functions
(e.g. the drive get-or-apply, the passive-drive check, the
dataclass-field dict, material paths).
- After merging the latest `develop` (including #8102), the tests above
were rerun and pass. `isaaclab_physx/test/sim/test_spawn_meshes.py` was
run with the `tetrahedralization` extra, which covers
`_tetrahedralize_surface`; it passes on this branch and on `develop`.
- `ruff check` and `ruff format` are clean.

## 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`
- [ ] 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
- [x] I have added a changelog fragment under
`source/<package>/changelog.d/` for each changed package (see
[Changelog](https://isaac-sim.github.io/IsaacLab/main/source/refs/contributing.html#changelog))
- [x] If this is a user-visible change, the fragment clearly describes
it


## Release backport

- [x] <!-- backport-active-release --> Backport this pull request to the
active release branch after it merges into `develop`
isaaclab-bot Bot pushed a commit that referenced this pull request Sep 28, 2026
# Description

Part 14 of the split of #7949. This PR cleans up sim authoring: the
schema writers in `isaaclab.sim.schemas`, the spawners, and the mesh
converter. It is rebased onto the current `develop` and ports only the
parts of #7949 that still apply there. The authored USD is unchanged,
and no public API changes.

**Spawners and mesh converter**
- `spawners.utils` (renamed from `spawners._utils`, see below) adds a
helper:
- `apply_schema_props` writes a spawner schema slot. It sends a fragment
mapping to the fragment writer, one entry at a time and anchored at the
spawned prim, and sends a legacy cfg to the legacy writer. The mesh and
shape spawners, `spawn_from_mesh`, and `MeshConverter` repeated this
branch for their collision, mass, and rigid-body slots; they now call
the helper.
- Material paths are resolved inline at each call site (absolute paths
are kept; relative names go under the spawned prim), replacing
`from_files._resolve_material_path`.
- Existing-prim checks are left to `create_prim`, which already raises
the same `ValueError`. The spawner bodies are shorter, and
`spawn_geom_from_prim_type` / `_spawn_mesh_geom_from_mesh` return the
prim they create.
- Mesh spawners share `_axis_transform` for the cylinder, capsule, and
cone axis rotation.
- Light, sensor, ground plane, and multi-USD spawners:
  - The ground plane hides its light with `set_prim_visibility`.
- `spawn_multi_usd_file` builds `MultiAssetSpawnerCfg` in one call and
no longer runs a `hasattr` check that was always true.
- `AssetConverterBase` has a shorter USD-extension and hash-file check,
and writes the `config.yaml` generation stamp with a single
`datetime.now()` call. The stamp format is the same.

**Schema writers**
- `apply_rigid_body_properties`, `apply_collision_properties`, and
`apply_mass_properties` now share `_apply_api_fragments`. Target
matching, `create_if_missing`, the zero-target warning, and result
aggregation are unchanged.
- The joint-drive writers share `_write_drive_attributes`, which
converts angular stiffness and damping from per-radian to per-degree and
maps `drive_type` to the USD `type` attribute. It is used by
`apply_drive`, `_ensure_drive_exists`, `apply_joint_drive_properties`,
and `modify_joint_drive_properties`. Joint-type detection reuses
`drive_instance_name`, including in `schemas_actuators`.
- Other shared helpers:
- `_write_mesh_approximation` writes and validates the
`physics:approximation` token.
- `_tetrahedralize_surface` holds the pytetwild call and the tet-winding
fix, moved out of `define_deformable_body_properties`.
- Smaller cleanups:
  - `activate_contact_sensors` counts descendants with `Usd.PrimRange`.
- `schemas_actuators` uses `to_camel_case` instead of a private
camel-case regex.
- Redundant `pass` statements are removed from the fragment marker
classes.
- Fragment appliers are called directly as `cfg.func(cfg, prim_path,
stage)`. `@configclass` already turns the `func` import string into a
callable `ResolvableString`, so the `callable(...) else
string_to_callable(...)` fallback (four schema writers and the
physics-material spawner) never ran.

**Reflection**
- Replaced avoidable `getattr`/`setattr`/`hasattr` with direct attribute
access or an `isinstance` check wherever the attribute is always defined
for the configs that reach that code (actuator authoring, the USD-file
and shape spawners, the camera spawner's intrinsics, the fragment
annotation lookup). The remaining uses are dynamic by design: iterating
dataclass fields, the deprecated alias forwarding, optional class
metadata, and mixed config types in the multi-asset wrapper.

**No underscore module names or cross-file private helpers in core**
- Renamed the underscore modules: `sim/spawners/_utils.py` → `utils.py`,
`sim/schemas/_backend_hooks.py` → `backend_hooks.py`,
`actuators/_compat.py` → `actuator_compat.py`, `benchmark/_cli.py` →
`cli.py`, `cloner/_fabric_notices.py` → `fabric_notices.py`,
`utils/_device.py` → `device.py`. All importers in `isaaclab`,
`isaaclab_physx`, `isaaclab_newton`, `isaaclab_ov`, the benchmark
scripts, tests, and docs are updated.
- Dropped the `_` prefix from module-level functions used from other
files: `limits_equal`, `resolve_limit_aliases`, `skip_joint_drive`,
`articulation_root_companion_namespace`, `apply_namespaced_schemas`,
`drive_instance_name`, `validate_newton_native_actuator_cfgs`,
`author_actuator_prims`, `resave_checkpoint_with_metadata`,
`deprecate_field_alias`, `deprecated_schema_cfg`, `spawn_from_usd_file`,
`spawn_geom_from_prim_type`. They are not added to the package exports.

**Not ported from #7949**
- The forwarding-shim refactors in `schemas/__init__.py` and
`schemas_cfg.py`, the `usd_api` / `physx_api` deprecation properties,
and the empty `_usd_*` metadata on deprecated cfgs. These are
deprecation-only and will be removed.
- The `_OVERRIDABLE_PROPS` constant in the wrappers, a single-use
constant.
- The import-only edits. `develop` already uses relative imports.

## Type of change

- Refactor (no functional change). The only visible difference is that
the `ValueError` for an unsupported deformable type is now one line,
instead of a triple-quoted string with embedded indentation.

## Verification

- USD authoring equivalence, comparing `develop` sources with this
branch in Kit, one fresh stage per case. All 57 cases produce identical
exported USDA, return values, and raised errors:
- Sphere, cuboid, cylinder, capsule, and cone shapes; and sphere,
cuboid, cylinder, capsule, cone, and rectangle meshes. Each is spawned
plain, with legacy cfgs, and with fragments (including dict targets and
absolute material paths).
- The cable spawner, `MeshFileCfg` from a trimesh object and from an
`.obj` file, and all five lights.
  - Pinhole and fisheye cameras, and the sensor frame.
- `UsdFileCfg` with legacy cfgs and with fragments (articulation root,
joint drives, contact sensors, semantics, materials), the
compliant-contact spawner, and a ground plane asset.
- `MultiAssetSpawnerCfg` and `MultiUsdFileCfg`, and a surface
deformable.
- The legacy `define_`/`modify_` writers and the fragment `apply_*`
writers, including `ensure_drives_exist`, the no-target case, mesh
collision with an invalid approximation name, and fixed tendons.
- A `MeshConverter` run with collision, mesh-collision, mass, and rigid
props and `make_instanceable`, comparing the generated USD layers and
`config.yaml`.
- Tests, run one file at a time; all pass:
- Spawners: `test_spawn_shapes`, `test_spawn_meshes`,
`test_spawn_lights`, `test_spawn_sensors`, `test_spawn_from_files`,
`test_spawn_wrappers`, `test_spawn_cable`, `test_spawn_materials`,
`test_default_ground_plane`, `test_visual_materials_kitless`.
- Schemas: `test_schemas`, `test_schemas_deprecation`,
`test_schemas_shim`, `test_schemas_kitless_fixed_joint`,
`test_schema_fragments`, `test_schema_fragment_parity`,
`test_schema_writer_nested_targets`, `test_joint_drive_fragments`,
`test_collision_fragments`, `test_mass_fragments`,
`test_mesh_collision_fragments`, `test_articulation_fragments`,
`test_material_fragments`, `test_tendon_fragments`,
`test_tendon_fragments_codeless`, `test_deformable_backend_split`.
  - Converter: `test_mesh_converter`.
- Actuator authoring: `source/isaaclab/test/actuators`,
`test_newton_actuator_manager_authoring`,
`test_newton_actuators_newton`, `test_newton_actuators_physx`.
- After the renames, reflection pass, and review fixes, the 57-case USD
comparison was rerun against the latest `develop` (0 differences), and
the spawner, schema, fragment, converter, cloner (`isaaclab` and
`isaaclab_physx`), benchmark CLI, device, Newton schema, and actuator
authoring tests were rerun; all pass. Every renamed module and its
importers in `isaaclab_physx`, `isaaclab_newton`, and `isaaclab_ov`
import cleanly.
- One-line helpers are kept inline rather than wrapped in functions
(e.g. the drive get-or-apply, the passive-drive check, the
dataclass-field dict, material paths).
- After merging the latest `develop` (including #8102), the tests above
were rerun and pass. `isaaclab_physx/test/sim/test_spawn_meshes.py` was
run with the `tetrahedralization` extra, which covers
`_tetrahedralize_surface`; it passes on this branch and on `develop`.
- `ruff check` and `ruff format` are clean.

## 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`
- [ ] 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
- [x] I have added a changelog fragment under
`source/<package>/changelog.d/` for each changed package (see
[Changelog](https://isaac-sim.github.io/IsaacLab/main/source/refs/contributing.html#changelog))
- [x] If this is a user-visible change, the fragment clearly describes
it


## Release backport

- [x] <!-- backport-active-release --> Backport this pull request to the
active release branch after it merges into `develop`

(cherry picked from commit d11f811)
ooctipus added a commit that referenced this pull request Sep 29, 2026
## Description

A follow-up to #8068, #8090, and #8102: it inlines or removes
launch-stack helpers whose names cost the reader more than their bodies
do. This PR is pure cleanup with no behavior change; every public
removal is listed below with its replacement.

### Launcher (`isaaclab.app.sim_launcher`)

- **Arguments become a dict once.** `launch_simulation` and `scan`
convert launcher args with `vars(args_cli)`, the namespace's live
`__dict__`, so values resolved at launch (for example
`args_cli.headless`) still reach the caller. That removes `_get_arg` /
`_set_arg` and their three-way Namespace/dict/None branching on every
read.
- **Resolved once instead of twice:**
- the livestream mode is resolved during argument normalization and read
back afterwards;
- the "explicit `--viz` overrides the config" rule for the Kit
visualizer is part of the scan's visualizer intent.
- **Inlined:** the logging-level resolver (now private here), the
one-line `_is_*` config predicates, and the Kit-source list formatter.
- **`make_physics_cfg` is private** (`_make_physics_cfg`); only `scan`
used it.

### Settings (`isaaclab.app.settings_manager`)

- **One accessor:** `SettingsManager.instance()` is removed. It was
identical to `get_settings_manager()`.
- **One setter:** `set_bool` / `set_int` / `set_float` / `set_string`
are removed. They only called `set()`, which already dispatches on the
value's type. Only calls on Isaac Lab's settings manager changed; calls
on carb's settings interface are untouched.

### Kit launcher (`isaaclab_physx.app.KitLauncher`)

- **Inlined:** three single-use helpers (spectator-view check,
experience dependency check, deferred CUDA device).
- **Merged:** the stop-button and play-button toolbar helpers become one
`_set_toolbar_button_visible`, with each button's current behavior kept.
- **Renamed:** `add_app_launcher_args` → `add_launcher_args`, and
`ExplicitAction` → `_ExplicitAction` (internal).

Net: +239 / −405 (about −165 lines).

## Type of change

- Breaking change (removed `SettingsManager.instance`, the typed
`SettingsManager.set_*` forwarders, and `isaaclab.app.make_physics_cfg`;
renamed `KitLauncher.add_app_launcher_args`)

## Validation

All runs on GPU 0.

- **Launcher and runtime tests:**
- Kit launcher (84), visualizer intent (8), distributed device
resolution (11), runtime compatibility (27), `require_kit` (10);
- preset Kit decision (6), preset CLI (11), RL entrypoints (44), CLI
tests (234);
- app tests, including the `AppLauncher` shim, exit status, and Kit
startup;
- simulation context (26), Isaac RTX renderer contract (26), and the
changed contact-sensor and OvPhysX scene-data tests.
- **`test_simulation_context_visualizers`:** 44 passed, 1 failed. The
failing test needs `carb`, which only CI's image has, and it fails
identically on develop.
- **Training:** Cartpole runs 2 iterations with `isaacsim_physx` (Kit
starts) and with `newton_mjwarp` (no Kit).
- **Tutorial:** `create_empty.py` reaches setup and runs.
- **Not run:** the toolbar-button and livestream `isaacsim.exp.full`
paths need a GUI or a livestream.
- **Removed test:**
`test_deferred_cuda_device_synchronizes_torch_and_warp`. Its helper is
now inlined into Kit-only startup code.

## Release backport

- [x] <!-- backport-active-release --> Backport this pull request to the
active release branch after it merges into `develop`

## Checklist

- [x] I have read and understood the contribution guidelines
- [x] I have run the pre-commit checks
- [x] I have made corresponding changes to the documentation
- [x] My changes generate no new warnings
- [x] I have added changelog fragments for every touched package
isaaclab-bot Bot pushed a commit that referenced this pull request Sep 29, 2026
## Description

A follow-up to #8068, #8090, and #8102: it inlines or removes
launch-stack helpers whose names cost the reader more than their bodies
do. This PR is pure cleanup with no behavior change; every public
removal is listed below with its replacement.

### Launcher (`isaaclab.app.sim_launcher`)

- **Arguments become a dict once.** `launch_simulation` and `scan`
convert launcher args with `vars(args_cli)`, the namespace's live
`__dict__`, so values resolved at launch (for example
`args_cli.headless`) still reach the caller. That removes `_get_arg` /
`_set_arg` and their three-way Namespace/dict/None branching on every
read.
- **Resolved once instead of twice:**
- the livestream mode is resolved during argument normalization and read
back afterwards;
- the "explicit `--viz` overrides the config" rule for the Kit
visualizer is part of the scan's visualizer intent.
- **Inlined:** the logging-level resolver (now private here), the
one-line `_is_*` config predicates, and the Kit-source list formatter.
- **`make_physics_cfg` is private** (`_make_physics_cfg`); only `scan`
used it.

### Settings (`isaaclab.app.settings_manager`)

- **One accessor:** `SettingsManager.instance()` is removed. It was
identical to `get_settings_manager()`.
- **One setter:** `set_bool` / `set_int` / `set_float` / `set_string`
are removed. They only called `set()`, which already dispatches on the
value's type. Only calls on Isaac Lab's settings manager changed; calls
on carb's settings interface are untouched.

### Kit launcher (`isaaclab_physx.app.KitLauncher`)

- **Inlined:** three single-use helpers (spectator-view check,
experience dependency check, deferred CUDA device).
- **Merged:** the stop-button and play-button toolbar helpers become one
`_set_toolbar_button_visible`, with each button's current behavior kept.
- **Renamed:** `add_app_launcher_args` → `add_launcher_args`, and
`ExplicitAction` → `_ExplicitAction` (internal).

Net: +239 / −405 (about −165 lines).

## Type of change

- Breaking change (removed `SettingsManager.instance`, the typed
`SettingsManager.set_*` forwarders, and `isaaclab.app.make_physics_cfg`;
renamed `KitLauncher.add_app_launcher_args`)

## Validation

All runs on GPU 0.

- **Launcher and runtime tests:**
- Kit launcher (84), visualizer intent (8), distributed device
resolution (11), runtime compatibility (27), `require_kit` (10);
- preset Kit decision (6), preset CLI (11), RL entrypoints (44), CLI
tests (234);
- app tests, including the `AppLauncher` shim, exit status, and Kit
startup;
- simulation context (26), Isaac RTX renderer contract (26), and the
changed contact-sensor and OvPhysX scene-data tests.
- **`test_simulation_context_visualizers`:** 44 passed, 1 failed. The
failing test needs `carb`, which only CI's image has, and it fails
identically on develop.
- **Training:** Cartpole runs 2 iterations with `isaacsim_physx` (Kit
starts) and with `newton_mjwarp` (no Kit).
- **Tutorial:** `create_empty.py` reaches setup and runs.
- **Not run:** the toolbar-button and livestream `isaacsim.exp.full`
paths need a GUI or a livestream.
- **Removed test:**
`test_deferred_cuda_device_synchronizes_torch_and_warp`. Its helper is
now inlined into Kit-only startup code.

## Release backport

- [x] <!-- backport-active-release --> Backport this pull request to the
active release branch after it merges into `develop`

## Checklist

- [x] I have read and understood the contribution guidelines
- [x] I have run the pre-commit checks
- [x] I have made corresponding changes to the documentation
- [x] My changes generate no new warnings
- [x] I have added changelog fragments for every touched package

(cherry picked from commit 24e1b46)
ooctipus pushed a commit that referenced this pull request Sep 30, 2026
)

# Description

Part 7B of the camera performance series, based on the latest `develop`
(including #8039, #8042, and #8087). A sweep of the per-step hot path
found remaining host-device synchronizations and code that costs more
than it gives. This PR fixes the verified findings: it removes the
synchronizations, launch overhead, and dead or over-defensive code, and
fixes the verified bugs.

Profiling on `develop` motivated this. The GPU sat idle 20–27% of each
step, because the CPU blocked on synchronizations and then launched
hundreds of small kernels. On `Isaac-Velocity-Rough-AnymalD` (4096
environments), for example, the CPU was blocked 12.8 ms of a 25.4 ms
step.

## Changes

### Host synchronizations removed
- **Episode logging:** the command, termination, and curriculum
managers, `survival_success_rate`, the pose commands, and task success
rates (Cabinet, Locomotion, Lift deformable/cable, Fourbar) log 0-d
device tensors instead of reading back `.item()`/`.cpu()` on every
reset. `RewardManager` already logged tensors, and the RL wrappers
accept them. Curriculum states are logged as copies, because rsl_rl
keeps episode infos for a whole rollout. The inspection accessor
`get_active_iterable_terms()` still returns Python scalars.
- **Velocity command:** heading control is branchless (`torch.where`)
instead of `is_heading_env.nonzero()` every step. Output is identical.
- **Event ranges:** core and Lift event terms use
`sample_uniform_from_ranges()` to draw named components directly. One
private, bounded bounds cache replaces both sets of tensor-building
helpers; it is keyed by values, resolved device, and dtype. Curriculum
edits take effect on the next call, and every call draws fresh samples
in the existing order.
- **Ray caster:** `reset()` no longer uploads its range tensor or
samples drift when the drift ranges are zero (the default). In rough
locomotion this reset runs almost every step. When drift is disabled
after sampling, reset clears the selected environments’ old drift.
- **DiffIK:** the `if ee_quat.norm() != 0` guard in `apply_actions`,
which synchronized every physics substep, is now a `torch.where` with
the same all-or-nothing behavior.
- **Direct tasks:** Reorient (about 14 host-list indexes per substep),
Cabinet, and Locomotion store joint and body indices as device tensors,
precompute static joint limits and default positions, and use the
non-deprecated `actuators.target_command` API.
- **Other task terms:**
- Locomotion `walk_target_w` caches its device target (it synchronized 4
times per step).
- Reorient's `EpisodeErrorRecorder` uses `torch.where` with NaN
reductions instead of boolean masks.
- Lift reset events share the named-range sampler with the core event
terms.
- **Joint limits:** Newton, PhysX, and OvPhysX position-limit writes
reuse the clamping counter and read it back only when the resulting
message would be logged.

### Launch overhead
- **`RewardManager`:** each term writes its weighted value into a
step-reward matrix column, followed by one sum and one batched
episode-sum update. Reset is one mean and one fill. With 20 terms at
4096 environments, compute drops from 121 to 23 kernels (471 → 152 µs)
and reset from 80 to 4 kernels (504 → 49 µs).
- **`ObservationManager`:** the built-in noise functions
(`constant_noise`, `gaussian_noise`, `uniform_noise`) are out-of-place,
so the pre-noise clone and the second clone are skipped. Safe built-in
noise functions are identified during `_prepare_terms`; custom noise
functions, noise models, and modifiers still clone, including when a
caller replaces a noise function after setup. The noise sampling order
is unchanged.
- **`CircularBuffer`:** one staged shift replaces K−1 slot copies for
frames up to 2 MiB. Image-sized frames keep the slot loop, which is
faster for them.
- **Lift contacts:** one stacked norm replaces per-sensor norms.
`contacts()` runs 5 times per step in Kuka Allegro, and this removes
about 80 kernels per step.
- **ANYmal symmetry augmentation:** one cached permutation and sign
tensor per transform, bitwise-equal to the original, is 1.56 → 0.15 ms
per call at 4096 environments.
- **RTX renderer:** normals, motion vectors, `rgb_hdr`, and simple
shading no longer copy each frame into a contiguous channel slice before
reshaping.
- **Smaller changes:**
- Reorient reads its command slices directly instead of concatenating
them.
  - sb3 computes `dones` on the host side.

### Overengineered code removed
- **`NewtonManager.step()`** ran the solver-internals reset every
physics step. `forward()` already performs it when kinematics are dirty,
so the extra call only launched with an all-False mask.
- **`WrenchComposer.reset()`** returns early when inactive. Its buffers
are already zero, and this removes about 7 memsets per rigid object per
physics step (21 µs → 0.07 µs per call).
- **`SensorBase`:** sensors with `debug_vis=True` no longer refresh on
every physics substep. That refresh existed because debug callbacks once
read private data. The callbacks now refresh the outdated buffers
themselves. The original eager update was introduced in commit
`4565902f5` to make visualization work without a `data` read; it did not
pump rendering.
- **`VisualizationMarkers.visualize()`** returns early when no backend
is active.
- **Dead code:** removed the Newton Warp renderer's output copy loop
(every output is a zero-copy alias), the impossible PhysX ray-caster
guard, and the redundant Lift `body_state_b` index conversion (redundant
since #8087).

### Bug fixes
- **Newton `body_com_acc_w`:** the finite difference divided by the
physics timestep even when an update spans several physics steps (Newton
owning the decimation loop). That made the acceleration, and
`body_lin_acc_l2`, `decimation`× too large. It now uses the elapsed
time, matching `joint_acc`. The OvPhysX rigid-object implementation uses
the same elapsed-time formula. PhysX and OvPhysX articulations read
acceleration from the backend, so they do not need this
finite-difference change.
- **`reset_root_state_uniform`** built its range tensors in `__init__`
and ignored call-time `pose_range`/`velocity_range`, so curriculum
updates were silently dropped.

### Investigated and not included
- **Step reset, command resample, and interval-event `nonzero`:** these
are now the only synchronizations left in
`Isaac-Velocity-Rough-AnymalD`. Each converts a mask into
variable-length environment IDs consumed across the reset, command, and
event APIs. Removing them requires a mask-based API redesign and is left
for a separate design.
- **Needs a Kit trace before changing:** the PhysX IMU/PVA per-update
COM upload, PhysX contact-sensor per-substep fetches, and the Fabric
render synchronizations. They rely on PhysX/Fabric semantics we have not
measured. A similar "pageable upload synchronizes" assumption proved
false for the Newton sensor flags.
- **Deferring the MPM sparse-grid status check:** a solver reset clears
the status, so a deferred check could lose capacity errors, and no test
covers this path.
- **RMPflow guard:** it already runs on the host, so removing the guard
gains nothing. The `joint_actions` allocation is needed for LEAPP export
tracing.
- **Lower confidence, left out:** collection mask-write kernels and
batching model-change notifications.

## Type of change

- Bug fix (non-breaking change which fixes an issue)
- Behavior changes with migration notes in the changelog fragments:
- Episode log values are 0-d tensors; use `float()` or `.item()` where a
Python number is needed.
- Reorient direct index attributes (`actuated_dof_indices`,
`finger_bodies`, `finger_wrench_bodies`) and `resolve_actuated_tendons`
ids are tensors.
- `EpisodeErrorRecorder` reports NaN for an empty selection after its
first update.
- Custom sensors should refresh their buffers inside
`_debug_vis_callback`.

## Performance

RTX PRO 6000 Blackwell Workstation Edition (driver 595.91.07); PyTorch
2.12.0+cu130; Warp 1.17.0; Newton 1.6.0; GPU otherwise idle. `develop`
at `60bc09f17` compared with this PR on that base, with the same
`uv.lock`. The branch has since merged `develop` at `f25d3f09b` (#8042,
#8090, #8102). The numbers below were measured before that merge; #8042
changes the Kuka camera observations and #8090 touches the simulation
context, so they have not been re-measured on the merged branch or after
review fixes. Static Lift success markers now resend positions to
support late backend activation; the earlier marker-index-only
optimization is not included. Runtime benchmark (`isaaclab benchmark
runtime`), Newton/MJWarp, seed 42, no visualizer. Three fresh processes
per revision, run in alternating order. Figures are the mean step time
across processes; every workload's per-process ranges are disjoint.

| Workload | `develop` | This PR | Change |
| --- | ---: | ---: | ---: |
| `Isaac-Ant`, 4096 envs | 3.369 ms | 2.629 ms | **−22.0%** (+28.1%
throughput) |
| `Isaac-Open-Drawer-Franka-Direct`, 4096 envs | 11.106 ms | 10.100 ms |
**−9.1%** (+10.0%) |
| `Isaac-Reorient-Cube-Allegro-Direct`, 4096 envs | 27.114 ms | 25.221
ms | **−7.0%** (+7.5%) |
| `Isaac-Velocity-Rough-AnymalD`, 4096 envs | 23.078 ms | 22.573 ms |
−2.2% (+2.2%) |
| `Isaac-Lift-KukaAllegro-Camera`, 256 envs,
`presets=duo_camera,rgb128`, Newton renderer | 12.396 ms | 12.202 ms |
−1.6% (+1.6%) |

Per-process step times (`develop` → PR):
- Ant: `3.367, 3.359, 3.382` → `2.639, 2.638, 2.611` ms.
- Drawer: `11.113, 11.102, 11.104` → `10.099, 10.100, 10.100` ms.
- Reorient: `27.136, 27.136, 27.070` → `25.217, 25.250, 25.197` ms.
- AnymalD: `23.081, 23.050, 23.103` → `22.551, 22.568, 22.599` ms.
- Camera: `12.389, 12.374, 12.424` → `12.373, 12.119, 12.113` ms.

Host synchronizations per `env.step()`, attributed to call sites with
PyTorch sync-debug mode over 170 steps:

| Task | `develop` | This PR | Remaining |
| --- | ---: | ---: | --- |
| `Isaac-Velocity-Rough-AnymalD` | 6.75 | 3.00 | step reset, command
resample, and interval-event `nonzero` |
| `Isaac-Lift-KukaAllegro-Camera` | 2.11 | 2.04 | step reset and command
resample `nonzero`; curriculum difficulty `.item()` (0.04 per step) |

PyTorch labels sync-debug mode a prototype that does not detect every
synchronization, and it does not see synchronizations issued by Warp, so
these counts are lower bounds.

## Validation

- The shared sampling refactor passed 257 tests across the existing
math, root-state event, and Lift configuration suites. The existing
event test now checks exact seeded samples, key order, missing axes,
fresh draws, range edits, and warm-cache CUDA synchronization. Eight
standalone equivalence checks also passed across CPU/CUDA,
float32/float64, and flat/nodal batch shapes. No new tests or test files
were added.
- Before the shared-sampler refactor, 205 focused tests passed, with
each file run in its own process: curriculum, observation, reward, and
termination managers; heading commands and mutable event ranges; DiffIK;
wrench composition; ray-caster and sensor-base behavior; marker
visualization; Lift and reorientation helpers; and the existing
joint-limit tests on both PhysX and OvPhysX, on CPU and CUDA. Kit tests
used the `isaacsim` extra, and OvPhysX used `ovphysx` with separate
CPU/GPU processes.
- Regression checks fail with the old curriculum accessor, stale drift,
and static marker behavior restored. Mutations also turn the retained
tests red for custom-noise aliasing, eager debug-visualization
refreshes, partial reward reset corruption, and missing reorientation
NaN statistics.
- Applied the test-audit skill: seven standalone tests were consolidated
into existing reward, wrench, reorientation, marker, ray-caster,
sensor-update, and Lift tests. Synchronization tests run on CUDA instead
of repeating the same bookkeeping on CPU. No new test functions or files
were added in the review-fix commit.
- Retained unique heading, curriculum snapshot, mutable-range, and
ANYmal symmetry coverage: there is no equivalent existing owner test.
The symmetry test checks the new permutation tables against the previous
independent index-assignment formulation.
- `uv run isaaclab -f` passed, including changelog checks against the
actual PR base, and `git diff --check` passed.
- The full test suite and training smoke runs have not been repeated on
this revision; benchmark figures above describe the earlier measured
revision.

## Release backport

- [x] <!-- backport-active-release --> Backport this pull request to the
active release branch after it merges into `develop`

## Checklist

- [x] I have read and understood the contribution guidelines.
- [x] I have run the pre-commit checks with `uv run --frozen isaaclab
-f`.
- [x] I have made corresponding changes to the documentation.
- [x] I have added tests that prove the behavior and checked the
regressions against the unfixed code.
- [x] I have added a changelog fragment for every touched source
package.
- [x] My name already exists in `CONTRIBUTORS.md`.
@ooctipus
ooctipus deleted the octi/launch-simulation-tests branch October 14, 2026 14:28
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

asset New asset feature or request isaac-mimic Related to Isaac Mimic team

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants