Repository navigation
Conversation
A mask-only Articulation.reset called ActuatorCollection.reset(None), which cleared delay buffers, network history, and Newton-native actuator state for every environment. Newton rigid objects and collections dropped the mask before resetting their wrench composers, clearing every environment's persistent wrench. ActuatorCollection.reset now takes env_mask. Isaac Lab actuator models still reset by index, so the collection converts the mask to indices once for all groups, and skips the conversion when no group carries reset state. Native actuator state resets from the mask through a new ActuatorControl.reset_native_actuators_mask hook; Newton and OVPhysX pass the mask to the Newton actuator adapter, and the base default converts to indices so backends that only implement the index hook stay correct.
InteractiveSceneWarp.reset passed env_ids=None to deformable objects, surface grippers, and sensors on a masked reset, so every environment's sensor state was cleared on each partial reset, and it never reset cable objects. Forward the mask to every entity family in the stable scene's order. Surface grippers use their reset_mask method. Cable objects take env_mask like the other scene assets, so the scene does not convert the mask for a reset that holds no state.
RigidObjectCollection.write_body_*_to_sim_mask converted masks to indices with torch.nonzero before delegating to the index writers, so the mask writers synchronized the host and could not be captured in a CUDA graph, although their docstrings name them as the graph-capable path. They now launch masked kernels over the full environment-body grid, and body_ids selections resolve to a mask through a preallocated scratch buffer. BaseRayCaster.reset compacted a mask with nonzero before resampling drift. A masked reset now resamples every environment and keeps the new values where the mask is set; index and slice selections are unchanged.
isaac-sim#8079 added ObservationGroupCfg.history_order and taught the stable observation manager to skip it as a group setting. The Warp manager's skip list was not updated, so it parsed the field as a term and raised a TypeError for every observation group, and no manager-based Warp environment could be constructed. Skip history_order like the other group settings. The Warp manager raises NotImplementedError for any term with history, and history_order only takes effect with history, so a time-ordered group cannot be silently reordered wrong.
The fixed-tendon set_*_mask methods and write_fixed_tendon_properties_to_sim_mask converted masks to indices with torch.nonzero and delegated to the index writers, so they synchronized the host and could not be captured in a CUDA graph. Write the masked cells in-kernel instead: scalar and full data go into the staging buffers through masked kernels, and the property writer copies the masked cells of each staging buffer into its simulation binding. The mask compaction helpers have no remaining callers and are removed.
A masked Articulation.reset on the Newton-native actuator path allocated a per-DOF mask for every actuator on each call, and a masked ray-caster reset sampled drift with torch ops and uploaded its ranges, so neither could run inside a CUDA graph captured on Warp's stream. NewtonActuatorAdapter preallocates its per-DOF reset masks. BaseRayCaster resamples masked drift in a Warp kernel from per-environment random states seeded from torch's global generator at initialization, with the ranges passed as launch arguments; index and slice resets keep the torch path.
The Newton fixed-tendon position-target setters raised a host-side dirty flag, and write_data_to_sim scattered the buffered targets into MuJoCo's controls only while it was set. A setter recorded in a CUDA graph raised the flag once, at capture time, so later replays updated the target buffer on the device but never reached the solver. Write the buffered targets on every step whenever the articulation has tendon actuators, and drop the flag from the Newton setters. The write costs about 60 us per call on Shadow Hand; gating it on a device-side flag would need Newton's control-array mapping re-implemented in a kernel.
Replace ManagerCallSwitch with an environment-owned WarpGraphCache that follows the Newton core capture model: a stage is recorded through NewtonQueries.capture_graph with no eager warm-up, so the first execution is the launch of the new graph; recording starts at the first step; graphs are keyed by the pointers and scalar values of the stage arguments; and every graph is dropped when the physics backend rebinds its buffers or stops, so the next step records against the new buffers. Managers with a non-capturable term keep their stages eager through register_capturability. ISAACLAB_WARP_CAPTURE=0 runs every stage eagerly and ISAACLAB_SYNC_DEBUG=1 traps hidden host synchronizations in eager stages. The direct environment uses the same cache in place of capture_or_replay.
The manager-based Warp environments now reset through one mask-first pipeline: every reset selection is copied into an environment-owned mask that the recorded reset stages read, the scene and the Warp managers reset from that mask, and the episode-length buffer is cleared by a masked kernel. The mask is compacted to environment indices once per reset, and only when a stable command, curriculum or recorder manager has active terms. ManagerBasedEnvWarp.reset accepts env_mask. Each step makes one host check to decide whether any environment terminated: the index compaction when a manager needs indices, otherwise reset_buf.any(). DirectRLEnvWarp moves its reset out of the end-of-step graph into its own stage behind the same predicate. A static gate pins the sanctioned host synchronizations of the mask-first path, which also removes the device read from CircularBuffer.max_length. The Episode_Termination metric keeps averaging over every environment, as the stable manager does.
push_by_setting_velocity, apply_external_force_torque, reset_root_state_uniform and randomize_rigid_body_com kept scratch arrays and parsed ranges as attributes of the module-level function. The first call allocated them, which inside a recorded stage becomes graph-owned memory; the parsed ranges froze the first configuration; and the state was shared by every environment in the process. They are now ManagerTermBase classes that allocate in __init__ and read their ranges on every call. randomize_rigid_body_com offsets the center of mass it finds on its first call instead of accumulating, and apply_external_force_torque with zero ranges leaves the permanent wrench composer untouched. The material and mass adapters reject modes other than startup and are marked non-capturable. Startup terms no longer keep the event stages eager, since they never run inside one, and set_term_cfg re-resolves the term and drops the recorded event stages.
A manager with one non-capturable term used to run every stage eagerly. Managers now describe each stage as ordered steps, one per term plus their own setup and finalize kernels, and WarpGraphCache.call_steps records each run of consecutive capturable steps into one graph while the terms marked @WarpCapturable(False) run eagerly in place. Reward and termination terms write their own output columns and the finalize kernel combines the columns in a fixed order, so these stages evaluate the capturable terms first and need at most one eager group. Observation noise and events draw from the shared per-environment random state, and action terms are applied in order, so those stages keep the configured term order and split into contiguous runs. Observation terms with history run as eager steps instead of raising inside capture. Replacing a reward term rebuilds its steps and drops the recorded reward stages.
A term can be capture-safe for one configuration and not another: a reward that reads a ray-caster refreshes the sensor on the host and launches its query graph, which cannot run inside a recorded stage, while the same reward without a sensor can. WarpCapturable accepts a predicate over the term's parameters, and its guard raises only for the parameters the predicate rejects. The Warp managers resolve capturability with each term's parameters, and a class term may set _warp_capturable in __init__ to decide per instance. The sync gate's opt-out inventory also lists predicate annotations.
Stable environments tell the physics backend their decimation and, when the backend runs the whole decimation loop, apply actions, step the simulation and update the scene once per environment step. The Warp environments still looped once per physics step, so contact sensors sampled four times as often on Go2 and the contact history, air times and the rewards and terminations reading them differed from the stable environment with the same configuration. Mirror the stable loop in the manager-based and direct Warp environments. The per-physics-step loop remains for backends that do not run the decimation loop.
The stable environments honor compute_final_obs: a step that resets environments stores the observations from before the reset in extras["final_obs"] and declares Same-Step autoreset in the metadata. The Warp environments ignored the flag, so torchrl, which sets it, bootstrapped time-outs from the post-reset observation. Compute the observations before the reset stage and keep a copy, because the reset overwrites the persistent observation buffers. Declare Same-Step autoreset in both Warp environment classes.
Warp managers and tasks log persistent buffers that later steps overwrite. Loggers such as the rsl_rl logger keep extras["log"] of every step and average it at the end of an iteration, so every entry showed the latest value. The stable environments log fresh tensors. The manager-based environment copies the tensor values when it builds the reset log. The direct environment keeps the log the task writes and returns a copy of it from every step, so task code that is recorded into a CUDA graph keeps refreshing its own buffers.
ManagerBasedEnvWarp built its reset log from the same persistent manager buffers as the RL environment, so a logged value changed with the next reset. Copy the tensor values like the RL environment does.
Warp environments now build the experimental CommandManager and record its compute and reset stages. The frontend swaps command term class_types for their Warp twins (commands join WARP_MANAGED_GROUPS); the term configs stay the stable ones. Add Warp twins of UniformVelocityCommand, UniformPoseCommand and NullCommand. The pose twin keeps the per-episode sticky success flag that the pose_command_success termination writes, checks both the position and the orientation thresholds, and logs Metrics/success_rate like the stable term. The null twin never resamples, so its time_left stays finite. The debug visualization of the stable velocity and pose commands moves into private mixins shared with the twins, keeping marker_pos_offset and environment_ids. Warp reward and observation terms read the command through CommandManager.get_command_wp on every call instead of caching the first environment's buffer in a function attribute.
The Warp observation manager infers the output size of a ray-caster term from the sensor's ray count, so height_scan needs no IO descriptor. The Newton ray caster refreshes through host-side timestamps and scene transforms, so height_scan is marked non-capturable and runs eagerly. terrain_out_of_bounds reads the terrain bounds from the environment on every call instead of caching the first environment's in function attributes. The rough velocity tasks keep the stable terrain curriculum and now adapt under --frontend warp.
A term defined outside the mirrored packages has no twin. The frontend now uses it unchanged when it is a Warp term: a class term that subclasses the Warp ManagerTermBase, or a function term that declares its capture behavior with WarpCapturable. Any other such term is reported as not a Warp term instead of being replaced by an unrelated built-in twin of the same name.
Reading a Newton ray caster refreshes it on the host and launches its own query graph, which fails inside a recorded reward stage: Go2 Rough stopped at its first captured step with CUDA error 900. base_height_l2 now declares itself capturable only without a sensor, so the reward stage records its other terms and runs this one eagerly; Go2 Flat keeps it recorded. The height-scanner case of the capture harness becomes an eager parity test, since that configuration is never recorded.
A recorded stage keeps the scalars a term read while it recorded, so a configuration change reaches a captured stage only through a setter that drops the recorded graphs. The Warp termination and command managers gain set_term_cfg and get_term_cfg that do so, matching the reward and event managers. The stable modify_term_cfg curriculum writes into a manager's term configuration directly. After writing a value it now hands the term configuration back to the manager's set_term_cfg when the manager has one, which is a no-op for the stable managers and records the Warp stages again. Capture-then-mutate tests cover a reward weight raised from zero, a replaced termination threshold, and a command range changed by modify_term_cfg.
The migration guide describes the stable-config frontend, the command terms, the author contract for recorded stages and Warp terms from other packages. The environment overview lists the rough velocity tasks and the stages that stay eager.
A recorded observation stage keeps the noise parameters its terms read while it recorded, so an observation noise curriculum written through modify_term_cfg, like the noise ADR terms of the Lift task, changed nothing once the stage was captured. The Warp observation manager gains set_term_cfg and get_term_cfg, naming a term "<group>/<term>". Setting a term prepares it again and drops the recorded observation graphs. The per-term preparation moves out of _prepare_terms so both paths share it; an existing noise model keeps its state. modify_term_cfg hands observation terms back under that name. A capture-then-mutate test changes a noise bias through modify_term_cfg after the observation stage recorded; it fails without the setter.
Curricula such as modify_reward_weight set their term on every reset past their threshold, and set_term_cfg dropped the manager's graphs on every call, so the reward stage was recorded again on every reset step. The reward stage now checks zero weights on the device: while recording, each term runs under a conditional graph node on an int32 flag kept beside the weights, so any weight change, including to and from zero, is a device write. Without conditional node support (CUDA < 12.4) the record-time check stays and a zero crossing records again. The reward, event, termination, command and observation setters compare the term against the values taken when the steps were built, since curricula change the stored configuration in place, and drop the graphs only when what the recorded steps read differs: the function, parameters and capturability, an event term's trigger settings, and the whole configuration of command and observation terms. An unchanged event or observation term is no longer resolved again, which reallocated its Warp buffers.
…/warp-frontend-managers
…pers resolve_ids resolves ids or a boolean mask to ids on a device, as resolve_1d_mask does to a mask. resolve_1d_mask takes an optional scratch mask, copies a Warp mask on another device, and accepts a boolean torch mask on its device inside a graph capture. The experimental package now re-exports it instead of keeping a copy.
…red helpers SensorBase, Camera and WrenchComposer resolve env_ids, env_mask and body_ids through resolve_ids and resolve_1d_mask instead of their own copies. The test doubles that build these objects without their initializer get the env masks a real sensor creates.
The nodal mask writers of BaseDeformableObject, which the OVPhysX deformable object inherits, and of the PhysX deformable object called wp.nonzero on a Warp array, which raised a RuntimeError. They now resolve the mask to ids with resolve_ids and call the index writers.
…sk writers through masks The Newton assets resolve ids and masks through resolve_ids and resolve_1d_mask. The RigidObjectCollection mask writers and the fixed-tendon mask setters write through the masks instead of converting them to ids, which waited for the GPU on every call; the joint-coordinate writes reuse a scratch mask; one generic masked 2-D write kernel replaces two copies.
The OVPhysX assets resolve env, body, joint and tendon selections through resolve_ids and resolve_1d_mask instead of their own copies.
The PhysX assets resolve env, body, joint and tendon selections through resolve_ids and resolve_1d_mask instead of their own copies. The sensor test doubles get the env masks a real sensor creates.
…-native isaac-sim#8821 owns the Newton mask writers: the collection write_body_*_to_sim_mask methods, the fixed-tendon mask setters and their shared masked-copy kernel. This branch takes its version of those and keeps only the reset-mask plumbing and the tendon-target capture fix in the shared files.
…-shared # Conflicts: # source/isaaclab/isaaclab/sensors/ray_caster/base_ray_caster.py
…leanup # Conflicts: # source/isaaclab_experimental/isaaclab_experimental/managers/observation_manager.py # source/isaaclab_experimental/isaaclab_experimental/utils/__init__.pyi # source/isaaclab_experimental/isaaclab_experimental/utils/warp/utils.py
…tend-managers # Conflicts: # source/isaaclab_experimental/isaaclab_experimental/managers/observation_manager.py
Writing the buffered targets every step cost 2-4% physics step time for every articulation with tendon actuators. The only Warp caller, the Shadow Hand twin, sets its targets outside the captured stage, so the host flag still sees them.
SceneEntityCfg.resolve_warp fills joint_ids_wp, joint_mask_wp, body_ids_wp and body_mask_wp from the resolved selection. The Warp managers call it after resolve, so the experimental SceneEntityCfg subclass and the frontend's promotion of stable scene entity configurations are removed.
CapturedStage, captured, eager, reset_captured_stages, WarpCapturable and is_warp_capturable move unchanged from isaaclab_experimental to isaaclab.utils.warp.capture, with their tests. The unused warp_capturable decorator is dropped; WarpCapturable covers it.
Its Warp terms only switch imports to the stable SceneEntityCfg and capture module.
This was referenced Oct 27, 2026
This branch has not been deployed
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Review Map
--videoon the Warp frontend--- config: look: neo flowchart: curve: basis --- flowchart TD P1("<b>#8821</b> · Part 1<br/>id/mask helpers") P2("<b>#8824</b> · Part 2<br/>PhysX tendon masks") P3("<b>#6646</b> · Part 3<br/>masked resets") P4("<b>#8783</b> · Part 4<br/>WarpRng") P5("<b>#6607</b> · Part 5<br/>per-term capture") P6("<b>#8228</b> · Part 6<br/>commands, Rough") P7("<b>#6582</b> · Part 7<br/>Shadow twin") P8("<b>#7012</b> · Part 8<br/>--video, independent") P9("📌 <b>#8856</b> · Part 9<br/>stable SceneEntityCfg, capture") P1 --> P2 P1 --> P3 --> P4 --> P5 P5 --> P6 P5 --> P7 P6 --> P9 style P9 stroke-width:3pxSummary
Warp terms select joints and bodies through the stable
isaaclab.managers.SceneEntityCfg, and the CUDA graph capture utilities live inisaaclab.utils.warp.capture; the experimental copies are removed. Per-step code is unchanged.Description
SceneEntityCfg: fieldsjoint_ids_wp,joint_mask_wp,body_ids_wp,body_mask_wpandresolve_warp(scene), called by the Warp managers afterresolve; stable managers do not call it. Removed: the experimentalSceneEntityCfg,from_stableand the frontend's config promotion.isaaclab.utils.warp.capture:CapturedStage,captured,eager,reset_captured_stages,WarpCapturable,is_warp_capturable, moved unchanged with their tests.resolve_warptest; Warp step time / base: Ant 1.002, Cartpole 0.994, Cartpole-Direct 1.000, Reach-Franka 0.999, Rough-Go2 1.006.DirectRLEnvWarpon the stable base (12).Type of change
Release backport
mainScreenshots
Not applicable.
Checklist
Docker and GPU tests run on demand. Push the commits you want tested, then
comment
run-cion the pull request.pre-commitchecks withuv run isaaclab --formatsource/<pkg>/changelog.d/for every touched package (do not editCHANGELOG.rstor bump package versions — CI handles that)CONTRIBUTORS.mdor my name already exists there