Skip to content

[WARP] Cleanup Part 2: Make the Warp frontend mask-first#6607

Draft
hujc7 wants to merge 66 commits into
isaac-sim:developfrom
hujc7:jichuanh/warp-frontend-cleanup
Draft

[WARP] Cleanup Part 2: Make the Warp frontend mask-first#6607
hujc7 wants to merge 66 commits into
isaac-sim:developfrom
hujc7:jichuanh/warp-frontend-cleanup

Conversation

@hujc7

@hujc7 hujc7 commented Jul 19, 2026

Copy link
Copy Markdown
Collaborator

Review Map

PR Status Depends on Exact changes
#5504 Part 1: frontend bridge (stable ids on the warp runtime)
📌 #6607 Part 2: mask-first frontend (draft) #5504 link
#6646 Part 3: scene reset mask-native + capture-ready (draft) #6607 link
#6580 Part 4: OVPhysX backend (draft) #5504 link
#6611 Part 5: persistent-launch speedups (draft, absorbs #6551) #6646 link

Summary

isaaclab

  • Added cached zero-copy Warp views of scene and terrain origins, boolean-mask scene resets (reset(env_mask=...)), and mask-based terrain curriculum updates; origin storage stays pointer-stable, including across reconfiguration.
  • Surface grippers consume boolean masks through reset_mask; scene construction no longer requires a published clone plan.

isaaclab_experimental

  • Removed ManagerCallSwitch, MANAGER_CALL_CONFIG, and the duplicate InteractiveSceneWarp; Warp environments instantiate Warp managers directly.
  • Centralized execution policy in the environment-owned WarpGraphCache: eligible CUDA stages warm up, capture, then replay; CPU devices, non-capturable managers, and direct-task stages stay eager.
  • Standardized partial resets on persistent Warp boolean masks; Torch IDs are materialized once per reset, only for recorders and legacy terms.
  • Added Warp-native command, curriculum, event, reward, observation, and termination paths with pointer-stable buffers.
  • Added a mask-boundary gate: any production nonzero( compaction must carry a reviewed same-line # mask-boundary: marker.
  • Review round: Warp-native attribute naming in command terms (*_torch view suffix), mask-based reset_to, _reset_mask orchestration, an ISAACLAB_WARP_CAPTURE=0 force-eager toggle with captured_stages introspection, and command debug-visualization folded into shared core mixins.

isaaclab_newton and isaaclab_tasks_experimental

  • Forwarded mask resets through Newton assets and the actuator adapter; Newton-native mode no longer resets Lab actuator state for all environments on partial resets.
  • Updated experimental tasks to the Warp-native MDP terms; Newton-native actuators stay opt-in.

Why

The Warp frontend crossed between Warp masks, Torch index tensors, stable-manager fallbacks, and a runtime dispatcher in high-traffic paths, adding host synchronization and hiding what was safe to optimize. This PR establishes one Warp-first path: direct Warp managers, mask-owned reset state, explicit host boundaries, and one graph cache that owns execution policy.

Design

  • Masks are canonical. Managers, scene elements, terrain updates, assets, and actuators consume boolean Warp masks; IDs exist only at marked host boundaries.
  • Managers are Warp-native. No runtime selection between stable and Warp managers.
  • Capture policy is centralized. The cache chooses eager or capture/replay from device and term metadata; call sites use plain function signatures.
  • Pointers remain stable. Buffers are allocated by their owners and updated in place.
  • Direct-task kernel stages stay captured; only the reset stage is eager. Action, pre-reset compute (dones/rewards), and observation stages capture as on develop. The reset stage (DirectReset_apply) is intentionally de-captured: develop kept it captured only by silently skipping Python-side actuator resets during replay — that capture was wrong, not merely unvalidated. [WARP] Cleanup Part 5: Speed up persistent Warp frontend launches #6611 restores its speed via record-replay. This is the only stage de-captured relative to develop; the manager-based path is a strict superset (command and curriculum stages are newly captured).

Performance

Measurements below used one NVIDIA L40, seed 42, serial execution, Newton MJWarp physics graphs enabled, and the Warp-eager frontend baseline. They characterize the mask-first/Warp-native cleanup; they do not isolate the newly centralized per-manager graph policy.

Develop comparison

Workload Envs Frames develop This PR Delta Peak GPU utilization
Go2 manager runtime 1,024 1,000 217,110 FPS 227,022 FPS +4.6% 61% -> 62%

Scaling

Cartpole is the upper line and Go2 is the lower line.

xychart-beta
    title "Warp-eager L40 throughput scaling"
    x-axis "Environments" [256, 1024, 4096]
    y-axis "Frames per second" 0 --> 1300000
    line [79521, 315642, 1248380]
    line [58942, 229836, 741682]
Loading
Task Envs Effective step FPS Peak GPU utilization
Cartpole Direct Warp 256 3.219 ms 79,521 23%
Cartpole Direct Warp 1,024 3.244 ms 315,642 23%
Cartpole Direct Warp 4,096 3.281 ms 1,248,380 25%
Go2 Flat Warp 256 4.343 ms 58,942 59%
Go2 Flat Warp 1,024 4.455 ms 229,836 63%
Go2 Flat Warp 4,096 5.523 ms 741,682 77%
Franka Reach Warp 1,024 3.826 ms 267,637 47%

Cartpole remains launch/CPU-bound across this range. Go2 shows diminishing frontend headroom as GPU utilization rises from 59% to 77%.

Validation

Grid: 300 iterations, 4096 envs, seed 42, rsl_rl, presets=newton_mjwarp; warp arms via --frontend warp (bridge from #5504, contained in this branch), torch anchors via --frontend torch. L40, solo GPU per run.

Task torch R / EL warp-capture R / EL warp-eager R / EL
Isaac-Cartpole 4.95 / 300.00 4.95 / 300.00 (CAP=15) 4.95 / 300.00
Isaac-Velocity-Flat-UnitreeGo2 35.30 / 995.76 35.28 / 987.61 (CAP=16) 36.32 / 1000.00
Isaac-Velocity-Rough-AnymalD 16.35 / 881.98 18.37 / 914.66 (CAP=12) 18.81 / 936.42
Isaac-Reach-Franka 0.51 / 360.00 0.44 / 360.00 (CAP=13) 0.46 / 360.00
  • Capture = eager within run-to-run noise. Contact-trivial tasks match exactly (Cartpole 3-way to the last digit). Contact-rich tasks are nondeterministic run-to-run regardless of mode: a 2x2 repeat study on Go2 (eager x2 / capture x2, same seed) gives fully overlapping distributions (eager alone spans 35.61-36.32). Root cause is contact-solver floating-point atomics, not capture: command RNG is device-side per-env state with write-back, both capture protocols execute stage effects exactly once, and replays return pointer-stable views.
  • Reach reward at a fixed 300-iteration snapshot is low-SNR. Warp repeats cluster 0.40-0.47 (same-arm spread +-0.02; capture and eager overlap at 0.44) vs a single torch anchor 0.51; episode length is exactly 360.00 in every arm, and the metric itself swings 0.51 -> 0.26 between 300 and 1000 torch iterations - cross-arm differences are far smaller than the metric's own along-curve variation.
  • Capture coverage is introspected, not assumed (WarpGraphCache.captured_stages): manager tasks capture 12-16 stages including CommandManager_compute/reset. On rough terrain the observation group runs eager by design - height_scan is marked warp_capturable(False) until the sensor lazy-update path is capture-audited (Part 3 scope); the rough terrain-levels curriculum term likewise registers eager.
  • Direct tasks capture exactly the 3 split stages (DirectAction_step, DirectEndPre_compute, DirectEndPost_step); DirectReset_apply is deliberately eager - the prior branch captured it only by silently skipping Python-side actuator resets on replay, which was wrong.
  • Tier-1: 16/16 runnable warp tasks pass capture+eager arms with introspected capture evidence.

Limitations and follow-ups

  • The empty-reset predicate synchronizes once on the host; currently cheaper than dispatching the no-op reset pipeline.
  • Trainer action ingress and Torch observation outputs remain public-interface boundaries.
  • The Newton-native actuator path needs separate profiling before becoming a frontend default.

Type of change

  • Bug fix
  • Experimental frontend API cleanup
  • Performance-oriented refactor

Checklist

  • I have read and understood the contribution guidelines.
  • All pre-commit hooks pass.
  • Generated API documentation is current.
  • I have added tests for the new reset and execution-policy behavior.
  • I have added a changelog fragment for every touched package.
  • My name already exists in CONTRIBUTORS.md.

hujc7 added 30 commits May 5, 2026 09:37
PR isaac-sim#5297 (Decouple Renderer from Camera) replaced
`sim.get_setting('/isaaclab/visualizer')` (returned a comma-separated
string) with `sim.has_active_visualizers()` plus per-type queries; the
warp env path was missed, so it crashes with
"AttributeError: 'dict' object has no attribute 'split'" during env
construction.

Mirror the stable env's pattern:
- `has_active_visualizers()` for the gating predicate
- `has_kit()` so kitless Newton-only runs (`--viz rerun`) skip
  ViewportCameraController
Warp's ObservationTermCfg / RewardTermCfg / TerminationTermCfg had no
behavioural difference from their stable counterparts — same fields,
same base class. The override only carried Warp-first docstrings while
keeping the classes as siblings of stable's, which broke
`isinstance(stable_term, warp.TermCfg)` checks inside the experimental
managers when a stable cfg is fed through the warp runtime.

Re-export stable's term cfg classes directly. Term funcs still follow
the warp-first `func(env, out, **params)` signature at runtime; the
type annotation just lives on stable's class instead of warp's.
Lets a stable manager-based RL env cfg run on the experimental warp
runtime without a separate warp task registration. Replaces the
duplicate per-robot warp env cfgs by adapting the stable cfg in place.

Components:
- isaaclab_experimental/envs/warp_frontend.py: 'WarpFrontend' walks the
  stable cfg, swaps each `term.func` to its same-named warp twin (only
  accepting candidates whose `__module__` lives under the warp packages
  — the warp mdp module re-exports stable terms via `from … import *`,
  so a naive `getattr` would silently keep the stable function), swaps
  each action `class_type`, picks the `newton` field of any PresetCfg,
  drops sensors with no warp counterpart (`height_scanner`), and
  in-place class-promotes `SceneEntityCfg` instances so warp kernels
  see the `joint_mask` / *\_ids_wp` cached fields. A
  `WarpAdaptReport` records every term that had no warp twin and
  surfaces them via the logger; `strict=True` makes the same condition
  raise `LookupError` instead.
- scripts/reinforcement_learning/rsl_rl/train.py: '--manager=warp' flag
  routes the constructed env through `WarpFrontend.build` instead of
  `gym.make`. The flag also auto-injects `presets=newton` into Hydra's
  argv so that PresetCfg wrappers resolve to the newton preset (Hydra's
  preset resolution runs *before* the adapter).

Validated: cartpole and Anymal-D Flat both pass a 3-way comparison —
- 'Isaac-Cartpole-v0' (stable manager): trains.
- 'Isaac-Cartpole-Warp-v0' (existing direct path): reward 0.06 / ep 76.
- 'Isaac-Cartpole-v0 --manager=warp': reward 0.06 / ep 76 (matches
  direct exactly).
- 'Isaac-Velocity-Flat-Anymal-D-v0 presets=newton': -8.45 / 191.
- 'Isaac-Velocity-Flat-Anymal-D-Warp-v0': -7.47 / 168.
- 'Isaac-Velocity-Flat-Anymal-D-v0 --manager=warp': -7.69 / 174 (within
  run-to-run variance of the direct warp path).

Both stable and warp frontends remain functional for every task; this
PR adds a flag-based selector without removing the existing direct
warp registrations.
Adds the isaaclab_experimental changelog fragment for the WarpFrontend
adapter and the --manager flag, and applies the ruff-format pass that
the pre-commit hook produced on warp_frontend.py.
The adapter is now a sequence of CompatRule objects (resolve preset, drop
sensors, promote SceneEntityCfg, swap mdp funcs, swap action class). New
incompatibilities are added by writing a small rule subclass instead of
editing the dispatcher.

The CLI flag is renamed --manager → --frontend because the dispatch also
covers direct envs: a stable manager-based cfg is adapted onto
ManagerBasedRLEnvWarp; a direct task is verified to point at a warp env
class and dispatched via gym.make. A stable direct cfg + --frontend=warp
raises IncompatibleEnvError with the offending entry_point and a hint at
the *-Direct-Warp-v0 alternative.

Other fixes:

- Forward render_mode through build() so --video keeps working.
- Attach the CompatReport on env.unwrapped.warp_compat_report so callers
  can inspect what was dropped or left unresolved.
- Assert the warp SceneEntityCfg subclasses the stable one before doing
  the in-place __class__ promotion; the rule fails loudly if the
  hierarchy is ever broken.
- Narrow the bare except in mdp-module discovery so real ImportErrors
  from broken cfgs propagate.
- presets=newton is now only auto-injected for stable manager-based
  tasks; direct warp tasks (which don't carry presets) are left alone.
- Warn when the user passes presets=<other> with --frontend=warp.
- Add the commands group to the rule that promotes SceneEntityCfg.
The earlier check inspected gym.spec(task).entry_point, which for stable
manager-based tasks is "isaaclab.envs:ManagerBasedRLEnv" — the env class
path, not the cfg path. So the startswith("isaaclab_tasks.manager_based")
test always failed and presets=newton was never injected. Hydra then
resolved every PresetCfg in the cfg tree (physics, contact_forces, etc.)
to its default field, leaving the warp runtime with PhysX class_types it
can't load.

Switch to spec.kwargs["env_cfg_entry_point"], which actually points at the
task cfg module (e.g. "isaaclab_tasks.manager_based.locomotion.velocity.
config.anymal_d.flat_env_cfg:AnymalDFlatEnvCfg"), and the prefix check
selects the right tasks.
The single-file warp_frontend.py grew into a real subsystem worth
splitting out, so move it into a frontend/ package with explicit
abstractions:

- frontend/base.py: Frontend ABC, CompatRule (check + transform via a
  unified run() method), TaskResolver (centralised gym.spec
  introspection -> TaskMeta), Workflow / Runtime / Severity enums,
  Issue / Change / Report record types, register_frontend / get_frontend
  registry. Helpers walk_attrs / resolve_warp_twin / iter_term_attrs are
  shared utilities used by rules.
- frontend/torch.py: TorchFrontend, the default. Pass-through to gym.make
  with one rule (WarnIfTaskIsWarpRegistered) for the contradiction case.
- frontend/warp.py: WarpFrontend with the full rule pipeline. Includes a
  new CheckPhysicsIsNewton blocking rule that surfaces the PhysX-with-warp
  incompatibility (asset class_type strings resolve to isaaclab_physx.*
  classes that depend on omni.physics.tensors.api, which the warp runtime
  does not initialise).

CLI: rename --frontend stable -> torch since the axis is *runtime*, not
*stability tier*. The frontend selector now reads cleanly:

  --frontend torch  ->  default gym.make path
  --frontend warp   ->  experimental warp runtime via WarpFrontend

train.py becomes a thin dispatcher: get_frontend(name) gives a Frontend
instance, frontend.preprocess_hydra_args(...) handles preset injection,
frontend.build(cfg, task) returns the env. No more inline conditional
imports; no more inline preset-injection logic.

env.unwrapped.frontend_report is the inspection point - callers and tests
can read what changed and what was missing without re-running adapt().

To add a new compatibility check, write a CompatRule subclass and append
it to the relevant frontend's `rules` tuple. To add a new runtime,
subclass Frontend and call register_frontend(name, cls).
- SwapMdpFunctions: skip terms whose ``func.__module__`` is already under
  the warp prefixes. Without this, running the bridge against a task
  already registered under ``isaaclab_tasks_experimental`` (e.g.
  ``Isaac-Cartpole-Warp-v0 --frontend=warp``) would silently drop terms
  whose warp twin happens not to live in the resolved fallback module.
  Also tighten ``_mdp_modules`` to require the trailing dot when matching
  ``isaaclab_tasks`` so we don't double-replace the prefix and end up
  importing ``isaaclab_tasks_experimental_experimental.*``.

- ResolvePhysicsPreset: scope to MANAGER_BASED via ``applies_to``. Direct
  cfgs aren't expected to carry ``PresetCfg`` wrappers; running this rule
  on them was a no-op but the scoping makes the contract explicit.

- CheckPhysicsIsNewton: positively accept ``isaaclab_newton.*`` modules,
  block on ``isaaclab_physx.*``, warn on anything else. The previous
  rule only rejected PhysX, so a custom or third-party physics cfg in an
  unrelated module would slip through silently.

- PromoteSceneEntityCfg: catch ``TypeError`` from the in-place
  ``__class__`` reassignment and surface it as a blocking issue.
  ``issubclass`` does not guarantee Python permits the layout change
  (slots, layout flags); failing loud at this seam is better than a
  cryptic crash mid-pipeline.

- WarpFrontend.preprocess_hydra_args: normalise leading dashes when
  inspecting ``presets=``, so ``--presets=foo`` is treated the same as
  ``presets=foo`` (Hydra accepts both forms).

- Frontend.resolve: hard-block when ``gym.spec`` returned no spec.
  Previously ``meta.runtime`` was ``UNKNOWN`` and ``construct`` would
  fail later with a less specific error; now the block fires before any
  rule runs.

- train.py: import the frontend lazily and tolerate ``ImportError``
  when ``--frontend=torch`` (the default). The experimental package is
  optional, so a missing install used to break the default path; now it
  falls back to ``gym.make`` for torch and only fails for ``warp``.

- frontend/__init__.py: trim ``__all__`` and the wildcard re-export so
  helpers (``walk_attrs``, ``iter_term_attrs``, ``resolve_warp_twin``,
  ``WARP_ROOT_PREFIXES``) are no longer advertised as the public framework
  surface. They remain importable from ``frontend.base`` for users
  writing their own rules.
- TaskResolver._classify_runtime: also accept class/callable entry points
  by inspecting __module__. gym.register accepts both ``"module:Class"``
  strings and class objects; the old check only handled strings, so a
  warp-registered task using the class form classified as
  Runtime.UNKNOWN and the warn / verify rules silently disengaged.

- SwapMdpFunctions._mdp_modules: narrow the exception match from
  ImportError to ModuleNotFoundError where ``exc.name`` matches the
  module being looked up. Previously a real ImportError raised inside
  an existing mdp module (broken import inside the package) would be
  silently swallowed and the rule would fall through to the fallback
  module, producing misleading "no warp twin" reports.
…ger-bridge

# Conflicts:
#	scripts/reinforcement_learning/rsl_rl/train.py
#	source/isaaclab_experimental/isaaclab_experimental/managers/manager_term_cfg.py
Collapses the CompatRule / Frontend / Report / Issue / Change framework
(509+72+513+95 LOC across base/torch/warp/__init__) into one module
(frontend.py, ~370 LOC). Net diff: 1270 → 864 LOC.

Behavior changes:
- Drop the pre-Hydra presets=newton injection. The warp frontend now
  hard-checks cfg.sim.physics is NewtonCfg at build time and tells the
  user to pass presets=newton on the CLI when it isn't.
- Drop DropUnsupportedSensors. The Newton RayCaster (isaac-sim#5510) makes the
  height_scanner case obsolete; any future incompatible sensor should
  fail loudly with the sensor name rather than silently set None.
- Merge SwapMdpFunctions + SwapActionClassType into one pass that
  swaps term.func or term.class_type uniformly for every group
  including actions.
- Missing warp twin is always a hard failure (no strict / non-strict
  toggle). Partial swaps would leave torch funcs in a cfg consumed by
  warp managers, which only accept the kernel-style signature.
- Replace SceneEntityCfg.__class__ = WarpSceneEntityCfg with a proper
  WarpSceneEntityCfg.from_stable() classmethod that copies every
  selection field through __init__.

Train.py is unchanged in shape — two call sites, now using the
top-level build(frontend, cfg, task_id) function.
Frontend (envs/frontend.py)
- Replace _TERM_PATHS hardcode (with its policy-only observation
  limitation) with _walk_terms: a recursive ManagerTermBaseCfg
  discovery that descends any configclass and yields each term
  with its path. New cfg layouts and observation sub-groups
  (perception, critic, camera_images, ...) are picked up
  automatically with no framework change.
- _promote_scene_entity_cfgs and _swap_mdp consume the new walker;
  delete _walk_attrs / _iter_term_attrs / _TERM_PATHS.
- Per-promotion log now lists the actual SceneEntityCfg paths.
- Rename _require_direct_is_warp_task to
  _assert_direct_warp_registration. Update _detect_workflow with a
  note for adding new cfg roots.

Manager term cfg contract
- Relax stable ObservationTermCfg / RewardTermCfg / TerminationTermCfg
  func annotation to Callable[..., torch.Tensor | None] so the warp
  func(env, out) -> None contract type-checks alongside the existing
  torch return-value contract.
- Drop the now-redundant warp-side manager_term_cfg.py shim and
  redirect the 9 relative imports onto isaaclab.managers.manager_term_cfg.

Tests
- New TestWalkTerms cases verify the recursive type-driven discovery.
- TestPromoteSceneEntityCfgs / TestSwapMdp use real term cfgs and
  configclass fixtures so they exercise the actual walker contract.
The isaaclab_experimental fragment shrinks from 47 lines of nested
narrative to 12 lines per the existing terse-fragment rule. Adds the
matching fragments for the two other touched packages: isaaclab (the
term-cfg type-hint relax) and isaaclab_tasks_experimental (.skip — the
humanoid import redirect is internal).
Two missed-sync fixes carried alongside the manager_based viewport
sync that already landed earlier in this branch:

1. DirectRLEnvWarp viewport controller now mirrors stable
   DirectRLEnv: '(has_gui or has_active_visualizers()) and has_kit()'
   gate, instantiate ViewportCameraController accordingly. Replaces the
   stale get_setting('/isaaclab/has_gui'/'render/offscreen') check and
   removes the commented-out instantiation that left
   viewport_camera_controller unconditionally None.

2. render(mode='rgb_array') in ManagerBasedRLEnvWarp and
   DirectRLEnvWarp now read SimulationContext.has_gui and
   has_offscreen_render properties instead of get_setting on keys that
   were removed when the simulation manager was refactored. Without
   this the gate silently fell through on None values.
The legacy bare 'newton'/'kamino' preset names were renamed on develop to
'newton_mjwarp'/'newton_kamino' to disambiguate from the Newton backend
label. Update the warp-frontend hard-check error message, the
`_adapt_cfg_for_warp`/`_require_newton_physics` docstrings, the
rsl_rl/train.py comment, and the matching test assertion to spell the
canonical preset name so users get the right CLI token in errors.
…ger-bridge

# Conflicts:
#	source/isaaclab_experimental/isaaclab_experimental/managers/__init__.py
#	source/isaaclab_tasks_experimental/isaaclab_tasks_experimental/manager_based/classic/humanoid/mdp/rewards.py
Move the stable->warp cfg adaptation out of the train.py --frontend path
and into ManagerBasedEnvWarp.__init__, so a task registered directly as
*-Warp-v0 (with env_cfg_entry_point pointing at the stable cfg) goes
through the exact same Newton-physics check, SceneEntityCfg promotion and
MDP twin swap. Twin lookup is now keyed off the stable cfg class module
(type(cfg).__module__) instead of the gym task id, so it no longer needs a
registration to resolve. Adaptation is idempotent.

This lets the warp side reuse stable env cfgs instead of carrying parallel
copies that drift.
Flatten isaaclab_tasks_experimental from the legacy manager_based/ |
direct/ | classic/ trees into core/<task>/ mirroring isaaclab_tasks/core,
so the frontend's stable->warp module-path rewrite resolves twins again
after the stable migration.

Manager-based warp tasks now reuse the stable env cfg (env_cfg_entry_point
points at isaaclab_tasks.core...; physics + MDP twins are swapped at
construction) instead of carrying parallel copies that drift; the drift
cfgs are deleted. Direct warp tasks keep their own env class + cfg.

Twin lookup keys off each stable symbol's own module, so ant reusing
manager_humanoid's mdp resolves with no per-task shim (mirrors core,
which gives manager_ant no mdp/ of its own).
Only observation/reward/termination/action terms are swapped to warp twins
and have their SceneEntityCfg promoted; event, curriculum, recorder and
command terms run on the stable (torch) managers, so their stable funcs are
left in place. This lets the warp env reuse the full stable cfg (domain
randomization events, reward curricula, etc.) instead of a trimmed copy.

Add a parametrized coverage test that, for every manager-based *-Warp-v0
task, loads its stable cfg and asserts adapt_cfg_for_warp succeeds (all
warp-managed twins resolve). Three tasks are xfail(strict) pending warp
twins: cartpole survival_success_rate, ant/humanoid body_incoming_wrench.

Also add the missing core/__init__.py so the registration walk descends
into the migrated package.
Stable cartpole was consolidated to unversioned ids (Isaac-Cartpole,
Isaac-Cartpole-Direct); mirror that on the warp side so each warp task
pairs 1:1 with its stable counterpart. Other tasks keep -Warp-v0 because
their stable ids still carry -v0 on develop.
Implements the two missing warp MDP twins so the full stable cfg adapts for
every manager-based *-Warp task (clears the xfails in the conversion test):

- cartpole `survival_success_rate`: ManagerTermBase reward mirroring stable —
  zero reward contribution, logs Metrics/success_rate (time-out rate) on reset.
- generic `body_incoming_wrench`: observation reading the Newton joint-wrench
  sensor and gathering per-body force+torque, following the existing
  sensor-reading twins (undesired_contacts/illegal_contact). Lives in the
  shared fallback mdp so it is task-agnostic.

Extends the observation manager's `body:N` out_dim resolution to also read
`sensor_cfg` (not just `asset_cfg`), since wrench obs select bodies via a
sensor entity.
The cfg walker only matched ManagerTermBaseCfg, but ActionTermCfg is a
separate base (not a subclass) carrying a swappable class_type. As a result
action terms kept their stable class and the warp ActionManager rejected
them ("not of type ActionType") when a *-Warp task reused a stable cfg.
Match ActionTermCfg in _walk_terms so action class_type is swapped too, and
guard it in the conversion test.

Also fix the cartpole warp registration agent entry points to the current
stable modules (rsl_rl_ppo_cfg:Cartpole[Direct]PPORunnerCfg, sb3_ppo_cfg)
after the cartpole consolidation renamed them.
The warp EventManager invokes term funcs with a Warp env-mask, so a stable
event func breaks at runtime (torch index by wp.array). Add events to the
warp-managed groups so their funcs are swapped to warp twins; only curriculum,
recorder and command managers fall back to stable. Cartpole (reset_joints_by_offset)
and reach now adapt and cartpole trains end-to-end on warp.

Rewrite cartpole survival_success_rate as a plain zero-reward term func: its
stable form logs a metric on reset via a host readback, which is incompatible
with the reward manager's CUDA-graph-captured reset; the warp twin keeps the
zero reward and omits the diagnostic metric.

Velocity tasks remain xfail in the conversion test pending warp twins for
randomize_rigid_body_mass / randomize_rigid_body_material (domain randomization).
Reconcile two conflicts from develop (45 commits):

- scripts/reinforcement_learning/rsl_rl/train.py: keep the --frontend=warp
  lazy-import build path; drop the branch-local fold_preset_tokens() call in
  favor of develop's verbatim remaining_args. isaac-sim#5944 reworked setup_preset_cli
  to resolve physics=/presets= tokens during Hydra resolution, so folding is
  no longer needed and the function was removed upstream.
- isaaclab_tasks_experimental .../direct/cartpole/__init__.py: keep the branch
  deletion (cartpole moved under core/); develop made no substantive change to
  the old-location file.
Develop's task-cleanup PRs dropped the -v0 suffix from the core Ant,
Humanoid, and Reach-Franka torch ids. The warp variants follow a fixed
pattern (torch id with -Warp inserted before any -Play/-v0 suffix), so
sync the six affected registrations and their doc references:

  Isaac-Ant-Warp-v0              -> Isaac-Ant-Warp
  Isaac-Ant-Direct-Warp-v0       -> Isaac-Ant-Direct-Warp
  Isaac-Humanoid-Warp-v0         -> Isaac-Humanoid-Warp
  Isaac-Humanoid-Direct-Warp-v0  -> Isaac-Humanoid-Direct-Warp
  Isaac-Reach-Franka-Warp-v0     -> Isaac-Reach-Franka-Warp
  Isaac-Reach-Franka-Warp-Play-v0 -> Isaac-Reach-Franka-Warp-Play

Cartpole already conformed; contrib and core-velocity tasks kept -v0
upstream, so their warp ids are unchanged.
Retain the current direct and manager-based experimental task layout and move the frontend integration to the unified RSL-RL entrypoint. Align the direct Warp Cartpole with the rewritten stable task.
…nager-bridge

# Conflicts:
#	source/isaaclab/test/test_reinforcement_learning_common.py
Define the flag once in add_common_train_args so every RL library's
train and benchmark entrypoint exposes it, instead of registering it
per-script for RSL-RL only. create_isaaclab_env now reads the argument
directly, so a caller that misses the shared registration fails loudly
rather than silently defaulting to the torch runtime.

Also drop the frontend dispatch from the deprecated rsl_rl/train.py
wrapper; deprecated entrypoints should not grow new features.
The Warp survival_success_rate twin wrote zeros and dropped the
Metrics/success_rate value that the stable class-based term flushes
into extras on reset, so torch and warp runs of the same task were not
comparable on the success metric.

Rebuild the twin as a Warp ManagerTermBase class that accumulates the
timed-out fraction of just-reset envs in device buffers and exposes it
as a persistent tensor view through the reward manager's reset extras.
Class reward terms may now return such views from reset(); the reward
manager merges them, which keeps the whole reset stage CUDA-graph
capturable (no host readback).
@github-actions github-actions Bot added the isaac-lab Related to Isaac Lab team label Jul 19, 2026
Keep Warp-native reset and manager paths mask-based through their host boundaries, and remove unsupported PhysX forwarding. Move frontend-only helpers into the experimental package, make terrain origin buffers dual-access, and keep capture state scoped to its owning environment.
@github-actions github-actions Bot added the documentation Improvements or additions to documentation label Jul 20, 2026
hujc7 added 4 commits July 20, 2026 00:45
Store terrain origin, level, and type wrappers as importer-owned members. Public properties now return the same persistent objects so ownership and pointer stability are explicit.
Keep the existing Torch terrain API while caching zero-copy Warp views during origin configuration. Route shared origin access through the Warp scene without changing the stable scene API.
Remove the experimental manager routing layer and make Warp managers the direct frontend implementation. Centralize capture eligibility in the environment-owned graph cache while preserving eager fallbacks for unsafe boundaries.
@hujc7 hujc7 changed the title Make the Warp frontend mask-first [WARP] Cleanup Part 2: Make the Warp frontend mask-first Jul 21, 2026
Remove the orphan isaaclab_tasks fragment and move the pose-command
quaternion fix entry to isaaclab_experimental. Route surface-gripper
mask resets through reset_mask, delegate the scene's Warp origins view
to the terrain owner, and make terrain origin reconfiguration
pointer-stable. Skip Lab actuator resets in Newton-native mode, share
one compact-ID materialization per reset between the recorder and
legacy curriculum terms, and downgrade the collection's mask-write
docstrings to match their host compaction. Add a static gate that
requires a reviewed mask-boundary marker on production nonzero sites.
hujc7 added 5 commits July 21, 2026 03:15
Split the direct-task step so pure-kernel stages capture as on develop
and only the reset stage stays eager, with its rationale registered at
the cache. Rename Warp command buffers to own the plain names with
_torch view suffixes, section their initialization, and fold the
command debug visualization into shared core mixins. Add a mask option
to reset_to, rename the manager reset orchestrator to _reset_mask,
reuse the action buffer across manager reloads, and drop redundant
scalar casts. Add captured-stage introspection, an environment-variable
force-eager toggle, curriculum manager section comments, and the
mask-first boundary conventions to AGENTS.md.
Rough-terrain tasks need a warp twin for the height_scan observation and
command-less tasks need a NullCommand twin once command class_types swap
to warp-native implementations. height_scan stays eager (warp_capturable
False) until the sensor lazy-update path is audited for graph capture;
its output width resolves through a new out_dim='sensor:rays' sentinel.
# Conflicts:
#	docs/source/overview/environments.rst
#	source/isaaclab_experimental/isaaclab_experimental/envs/mdp/events.py
#	source/isaaclab_experimental/isaaclab_experimental/envs/mdp/observations.py
#	source/isaaclab_experimental/isaaclab_experimental/managers/manager_term_cfg.py
#	source/isaaclab_experimental/isaaclab_experimental/managers/termination_manager.py
#	source/isaaclab_rl/test/test_entrypoints_common.py
#	source/isaaclab_tasks_experimental/isaaclab_tasks_experimental/core/reach/mdp/curriculums.py
#	source/isaaclab_tasks_experimental/isaaclab_tasks_experimental/manager_based/locomotion/velocity/config/anymal_b/rough_env_cfg.py
#	source/isaaclab_tasks_experimental/isaaclab_tasks_experimental/manager_based/locomotion/velocity/config/anymal_c/rough_env_cfg.py
#	source/isaaclab_tasks_experimental/isaaclab_tasks_experimental/manager_based/locomotion/velocity/config/anymal_d/rough_env_cfg.py
#	source/isaaclab_tasks_experimental/isaaclab_tasks_experimental/manager_based/locomotion/velocity/config/go1/rough_env_cfg.py
#	source/isaaclab_tasks_experimental/isaaclab_tasks_experimental/manager_based/locomotion/velocity/velocity_env_cfg.py
#	source/isaaclab_tasks_experimental/isaaclab_tasks_experimental/manager_based/manipulation/reach/reach_env_cfg.py
The warp env runs commands on the experimental captured CommandManager,
so stable command term class_types must be swapped to their warp twins
like every other warp-managed group. Stable CommandTermCfg is a
standalone base (like ActionTermCfg), so the term walker must match it
explicitly; without this the swap silently skipped command terms and
env construction failed with a CommandType TypeError.
The bridge merge moves experimental velocity/reach MDP modules under
isaaclab_tasks_experimental.core and retires reach_env_cfg; the
curriculum-manager and terrain-out-of-bounds tests import from the new
locations.
hujc7 added 10 commits July 21, 2026 12:56
Every @wp.kernel carries a purpose docstring (domain quantity, units,
launch dimensionality where non-trivial) so kernels self-explain
instead of reading as bare index math.
Play backends constructed environments with a direct gym.make, so
warp-trained policies could not be played through the official CLI:
the shared parser accepted --frontend but play ignored it. All four
dispatched play backends now build their environment via
create_isaaclab_env, mirroring their train twins' MARL-conversion
semantics, and a unit test pins the routing. The rlinf integration
constructs environments inside the external framework and remains
frontend-unroutable (documented in the changelog).
Fix the action-parity mock for origin-less tests, guard the clone-origin
warp view against reallocation, accept non-contiguous terrain origins,
and pin the reset quaternion composition against the stable math
reference. Replace the mask-boundary marker comments with a structural
gate: host syncs live in sanctioned functions listed exactly in the gate
test, ISAACLAB_SYNC_DEBUG runs warp stages under torch's sync-debug trap
as the runtime net, and every @WarpCapturable(False) opt-out is pinned
by an inventory test. Consolidate capturability to the class-aware
WarpCapturable decorator. The new gate immediately caught a per-step
host sync in CircularBuffer.max_length (read on the append path), fixed
by caching the host int. Classic-PhysX env_mask behavior is documented
on InteractiveScene.reset instead of guarded.
The Warp observation manager applies noise through the experimental
noise cfg family, so stable noise cfgs on bridged tasks were silently
skipped and training ran without the configured corruption. The
frontend now swaps stable constant/uniform/gaussian noise cfgs to their
warp-native twins during cfg adaptation (data fields copied, warp func
default); cfgs it cannot represent — class-based noise models,
customized funcs, tensor-valued parameters — are an aggregated hard
error instead of a silent MDP change. The observation manager rejects
non-warp noise cfgs at construction and plumbs the shared per-env RNG
state to function-style noise kernels, wiring the contract the noise
module documented. Audit: every function-style noise cfg in the repo
has a swappable warp twin; class-based models are loud unsupported.
Curriculum terms swap to warp twins when one exists, keeping the
legacy ID-based fallback for stable terms without twins; the mask-native
task classes take their stable functions' names (terrain_levels_vel,
modify_reward_weight) so plain name resolution finds them, and the
velocity legacy shim is removed with them. The curriculum manager's
legacy modes collapse to one (compact IDs, materialized at most once);
the unused requires_host_ids cfg knob is removed. Rough-terrain
curriculum now runs mask-native and captured; reach's weight curriculum
runs mask-native but stays eager by its own declaration (host step
counter). The non-capturable inventory detects attribute-style
annotations and scans the tasks package.
# Conflicts:
#	source/isaaclab_experimental/test/envs/test_frontend_cfg_conversion.py
#	source/isaaclab_rl/isaaclab_rl/entrypoints/backends/play_rl_games.py
#	source/isaaclab_rl/isaaclab_rl/entrypoints/backends/play_rsl_rl.py
#	source/isaaclab_rl/isaaclab_rl/entrypoints/backends/play_sb3.py
#	source/isaaclab_rl/isaaclab_rl/entrypoints/backends/play_skrl.py
Both tasks exercise the same converter mechanism (name resolution to a
mask-native class twin); one parametrized test covers both data points.
…nager-bridge

# Conflicts:
#	docs/source/overview/environments.rst
#	source/isaaclab_rl/isaaclab_rl/entrypoints/backends/play_rl_games.py
#	source/isaaclab_rl/isaaclab_rl/entrypoints/backends/play_rsl_rl.py
#	source/isaaclab_rl/isaaclab_rl/entrypoints/backends/play_sb3.py
#	source/isaaclab_rl/isaaclab_rl/entrypoints/backends/play_skrl.py
#	source/isaaclab_rl/test/test_entrypoints_common.py
Use the presets= Hydra group name in the PhysX limitation note and drop
performance rows for flat-velocity tasks removed by the catalog update
(AnymalB, AnymalC, A1, Go1); rename the Go2 row to the UnitreeGo2 task
name.
…nd-cleanup

# Conflicts:
#	docs/source/overview/environments.rst
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

documentation Improvements or additions to documentation isaac-lab Related to Isaac Lab team

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant