Skip to content

Step Newton with one program and one capture policy#6614

Draft
ooctipus wants to merge 1 commit into
isaac-sim:developfrom
ooctipus:zhengyuz/fix-deferred-graph-capture
Draft

Step Newton with one program and one capture policy#6614
ooctipus wants to merge 1 commit into
isaac-sim:developfrom
ooctipus:zhengyuz/fix-deferred-graph-capture

Conversation

@ooctipus

@ooctipus ooctipus commented Jul 20, 2026

Copy link
Copy Markdown
Collaborator

What

One step program and one capture policy for the Newton physics manager.

  • _simulate_full / _simulate_physics_only are replaced by ONE program: _simulate() runs steps x (collide -> actuator stage -> post-actuator hooks -> solver substeps) and a tail (post-step hooks, sensors). steps is decimation when the manager owns the loop and 1 when the environment does — loop ownership is data, not a second code path.
  • The CUDA graph machinery reduces to ONE deferral policy and ONE capture routine: every site that decides "the graph is stale but cannot be captured right now" (solver init, set_decimation, hard reset) calls _invalidate_graph(); the first step() afterwards captures. The init-time ScopedCapture branch, the RTX-vs-headless selector, set_decimation's inline recapture, and the duplicated Kamino pin-replay are deleted.
  • The capture warmup executes one true full step of the program, so the capturing step counts as a physics step (no double-advance). handles_decimation() reports loop ownership instead of aliasing kernel graph-safety.

Why

Scenes whose callers never set a decimation (plain SimulationContext loops) previously never got their CUDA graph and ran eager forever; fixing that minimally would have added a third arm to an already-forked capture decision. With one program and one policy, both historical failure modes — never-captured and back-to-back re-capture faults — are structurally unrepresentable: there is exactly one capture per invalidation and exactly one program to capture.

Evidence

  • Regression test (test_deferred_graph_capture.py): a fast-path scene that never sets a decimation gets its graph on the first step; verified to fail on the old code.
  • Newton manager abstraction suite: 80 passed on this branch (includes rewritten graph-flow tests asserting the capture tick does not double-advance).
  • No-regression check with the repository benchmark, identical command on base and branch (runtime.py --task Isaac-Velocity-Flat-AnymalD --num_envs 1024 --num_frames 300 --seed 7, RTX 5090): base 12.70 ms/iter, this PR 12.86 ms/iter (within run noise).

Test plan

./isaaclab.sh -p -m pytest source/isaaclab_newton/test/physics/test_deferred_graph_capture.py
./isaaclab.sh -p -m pytest source/isaaclab_newton/test/physics/test_newton_manager_abstraction.py
./isaaclab.sh -p -m pytest source/isaaclab_newton/test/physics/test_newton_solver_reset.py

🤖 Generated with Claude Code

@github-actions github-actions Bot added bug Something isn't working isaac-lab Related to Isaac Lab team labels Jul 20, 2026
NewtonManager ran two parallel step programs (_simulate_full,
_simulate_physics_only) and a capture decision forked three ways
(init-time wp.ScopedCapture, RTX deferral, Newton-actuator skip). The
skip arm relied on set_decimation to capture later, so callers that
never set a decimation - plain SimulationContext loops - ran eager
forever (measured 5.06 ms vs 0.91 ms per tick for the same workload).

Collapse both forks:

- _simulate() is the only step program, composed of _iteration_stages
  (collide -> actuators -> post-actuator hooks -> substeps) and
  _tail_stages (post-step hooks -> sensors). It runs the decimation
  loop when the manager owns it and exactly one sub-step otherwise;
  mixed scenes step the adapter eagerly and replay a per-iteration
  graph (_iteration_graph) inside the loop.
- handles_decimation() now follows the Newton actuator path (loop
  ownership), no longer aliased to graph-safety, so mixed scenes fold
  the decimation loop into one step() call as well.
- Decision sites (initialize_solver, set_decimation, hard reset) only
  call _invalidate_graph(): drop the graphs, owe a re-capture. The
  sole capture site is step()'s pending-consume block, and the one
  capture routine _capture_relaxed_graph(device, warmup, capture)
  works both headless and with RTX active. Its warmup runs _simulate
  eagerly and counts as the capturing tick's step: the execute branch
  is skipped and sim_time advances at a single site, so the first
  step never double-advances. The Kamino replay-once-to-pin lives
  inside the capture routine instead of being duplicated at two
  sites.

The never-captured-scenes bug is fixed as a corollary: decision sites
only invalidate and the first step captures, on every path. Exactly
one capture per invalidation also makes the back-to-back capture CUDA
faults of the contact + actuator pipeline structurally impossible.

The regression test (test_deferred_graph_capture.py) was verified to
fail against the original skip-instead-of-defer behavior.
@ooctipus
ooctipus force-pushed the zhengyuz/fix-deferred-graph-capture branch from 74ab0f3 to ff6344c Compare July 20, 2026 01:44
@ooctipus ooctipus changed the title Fix deferred CUDA graph capture on the Newton actuator path Step Newton with one program and one capture policy Jul 20, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

bug Something isn't working isaac-lab Related to Isaac Lab team

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant