Skip to content

Commit 574e92a

Browse files
committed
Fix Kit+Newton recording gap: skip Kit Replicator for Newton physics
Newton's Fabric transform writes (via wp.fabricarray Warp kernel) do not trigger RTX's scene delegate change notifications, so Replicator annotator buffers stay zeroed regardless of how many app.update() calls are issued. Fix _select_video_backend to detect Newton physics (video_capture_backend() == "newton_gl") and skip Kit Replicator in that case. If a Newton GL visualizer is configured, use its framebuffer directly. If only a Kit visualizer is configured alongside Newton, log a warning and fall back to standalone Newton GL capture, which renders Newton's scene correctly. Kit recording continues to work unchanged with PhysX. Add two unit tests for the new fallback dispatch and remove the xfail from test_kit_visualizer_newton_source_records_rgb — all 5 integration tests now pass (previously 4 passed, 1 xfailed).
1 parent 8b99f25 commit 574e92a

4 files changed

Lines changed: 70 additions & 30 deletions

File tree

source/isaaclab/changelog.d/mtrepte-refactor-viz-and-recording-cfg-classes.minor.rst

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -23,3 +23,8 @@ Changed
2323
* Removed ``eye`` and ``lookat`` fields from
2424
:class:`~isaaclab.envs.utils.VideoRecorderCfg`. The recorder is now passive: it records
2525
whatever the active visualizer or physics backend renders without repositioning the camera.
26+
27+
* When Newton is the active physics backend, :func:`~isaaclab.envs.utils.video_recorder._select_video_backend`
28+
now skips Kit Replicator capture (Newton Fabric writes do not notify RTX's scene delegate,
29+
producing black frames) and falls back to standalone Newton GL capture with a logged warning.
30+
Kit recording continues to work with PhysX.

source/isaaclab/isaaclab/envs/utils/video_recorder.py

Lines changed: 25 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -47,10 +47,32 @@ def _select_video_backend(scene: InteractiveScene, backend_source: str) -> tuple
4747

4848
if backend_source == "visualizer":
4949
visualizer_cfgs = scene.sim._resolve_visualizer_cfgs()
50-
for visualizer_type, backend in (("kit", "kit"), ("newton", "newton_gl")):
50+
physics_backend = scene.sim.physics_manager.video_capture_backend()
51+
52+
if physics_backend != "newton_gl":
53+
# PhysX: Kit Replicator and Newton GL visualizers both supported.
54+
for visualizer_type, backend in (("kit", "kit"), ("newton", "newton_gl")):
55+
for visualizer_cfg in visualizer_cfgs:
56+
if visualizer_cfg.visualizer_type == visualizer_type:
57+
return backend, visualizer_cfg
58+
else:
59+
# Newton physics: Kit Replicator recording does not work because
60+
# Newton's Fabric transform writes do not trigger RTX's scene
61+
# delegate — the annotator buffer stays black. Use Newton GL
62+
# capture instead (from an active Newton visualizer if present,
63+
# or standalone otherwise).
5164
for visualizer_cfg in visualizer_cfgs:
52-
if visualizer_cfg.visualizer_type == visualizer_type:
53-
return backend, visualizer_cfg
65+
if visualizer_cfg.visualizer_type == "newton":
66+
return "newton_gl", visualizer_cfg
67+
kit_requested = any(getattr(cfg, "visualizer_type", None) == "kit" for cfg in visualizer_cfgs)
68+
if kit_requested:
69+
import logging
70+
71+
logging.getLogger(__name__).warning(
72+
"Kit Replicator recording is not supported with Newton physics "
73+
"(Newton Fabric writes do not notify the RTX scene delegate). "
74+
"Falling back to standalone Newton GL capture."
75+
)
5476

5577
backend = scene.sim.physics_manager.video_capture_backend()
5678
if backend is not None:

source/isaaclab/test/envs/test_video_recorder.py

Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -80,6 +80,28 @@ def test_resolve_backend_selects_newton_visualizer():
8080
assert visualizer_cfg is newton_cfg
8181

8282

83+
def test_kit_visualizer_falls_back_to_newton_gl_when_newton_physics():
84+
"""Kit visualizer is skipped for Newton physics; falls back to Newton GL standalone."""
85+
kit_cfg = _make_visualizer_cfg("kit")
86+
scene = _make_scene([kit_cfg], video_backend="newton_gl")
87+
88+
backend, visualizer_cfg = _select_video_backend(scene, "visualizer")
89+
90+
assert backend == "newton_gl"
91+
assert visualizer_cfg is None
92+
93+
94+
def test_kit_visualizer_is_used_when_physx_physics():
95+
"""Kit visualizer is still preferred when PhysX is the physics backend."""
96+
kit_cfg = _make_visualizer_cfg("kit")
97+
scene = _make_scene([kit_cfg], video_backend="kit")
98+
99+
backend, visualizer_cfg = _select_video_backend(scene, "visualizer")
100+
101+
assert backend == "kit"
102+
assert visualizer_cfg is kit_cfg
103+
104+
83105
def test_resolve_backend_renderer_source_ignores_visualizers():
84106
"""Renderer source bypasses active visualizers and falls through to physics manager."""
85107
newton_cfg = _make_visualizer_cfg("newton")

source/isaaclab_tasks/test/core/test_recording_backends.py

Lines changed: 18 additions & 27 deletions
Original file line numberDiff line numberDiff line change
@@ -7,34 +7,29 @@
77
88
``_select_video_backend`` has three dispatch paths:
99
10-
1. ``"kit"`` visualizer present → Kit Replicator capture (IsaacsimKitPerspectiveVideo)
11-
2. ``"newton"`` visualizer present → Newton GL framebuffer (NewtonGLVisualizer.render_rgb_array)
12-
3. Neither above (any other visualizer or no visualizer)
10+
1. ``"kit"`` visualizer present + PhysX → Kit Replicator capture (IsaacsimKitPerspectiveVideo)
11+
2. ``"newton"`` visualizer present → Newton GL framebuffer (NewtonGLVisualizer.render_rgb_array)
12+
3. Neither above, OR Kit visualizer with Newton physics
1313
→ physics manager video_capture_backend():
1414
"kit" → standalone Kit capture
1515
"newton_gl" → standalone Newton GL capture
1616
None → RuntimeError (not tested here; covered by test_video_recorder.py)
1717
18+
When Newton is the physics backend, Kit Replicator recording is skipped
19+
(Newton Fabric writes do not notify RTX's scene delegate, producing black frames).
20+
_select_video_backend falls back to Newton GL standalone with a logged warning.
21+
1822
Path 3 fallthrough (Rerun, Viser, NewtonRTX visualizer types) is covered at the
1923
unit level by test_video_recorder.py::test_resolve_backend_unsupported_visualizer_falls_back.
20-
NewtonRTXVisualizerCfg is not yet a factory-registered type and cannot be
21-
instantiated as a visualizer, so it has no integration test here.
22-
23-
Known limitation (xfail): Kit Replicator recording does not produce frames when
24-
Newton is the physics backend. Kit recording relies on
25-
``ensure_isaac_rtx_render_update`` called by the PhysX manager's render
26-
callback; when Newton is active that callback is absent, so the render product
27-
buffer stays zeroed even though the Kit visualizer pumps ``app.update()``.
28-
Tracked as a known gap — not a regression introduced by this re-arch.
2924
3025
Setup:
3126
- AppLauncher(headless=True, enable_cameras=True)
3227
- CartpoleEnv (DirectRLEnv, 1 env) as the test vehicle.
3328
Tests:
3429
- KitVisualizerCfg + PhysX + backend_source="visualizer"
35-
-> Kit Replicator capture (path 1, PhysX physics) [passes]
30+
-> Kit Replicator capture (path 1) [passes]
3631
- KitVisualizerCfg + Newton + backend_source="visualizer"
37-
-> Kit Replicator capture (path 1, Newton physics) [xfail: see above]
32+
-> Newton GL standalone (path 3 fallback: Kit skipped for Newton physics) [passes]
3833
- NewtonGLVisualizerCfg + Newton + backend_source="visualizer"
3934
-> Newton GL framebuffer capture (path 2) [passes]
4035
- PhysX + backend_source="renderer"
@@ -130,17 +125,13 @@ def test_kit_visualizer_physx_source_records_rgb():
130125
_assert_valid_frame(frame, "kit-visualizer-physx")
131126

132127

133-
@pytest.mark.xfail(
134-
strict=True,
135-
reason=(
136-
"Kit Replicator recording produces a zero buffer when Newton is the physics backend. "
137-
"The PhysX manager registers an ensure_isaac_rtx_render_update render callback that "
138-
"primes the render product; Newton does not, so the buffer stays zeroed even though "
139-
"the Kit visualizer pumps app.update(). Known gap — not a regression of this re-arch."
140-
),
141-
)
142128
def test_kit_visualizer_newton_source_records_rgb():
143-
"""Kit visualizer + Newton physics → Kit Replicator capture stays all-black (known limitation)."""
129+
"""Kit visualizer + Newton + backend_source='visualizer' → Newton GL standalone (Kit skipped for Newton).
130+
131+
Kit Replicator recording does not work with Newton physics because Newton Fabric
132+
writes do not notify RTX's scene delegate. _select_video_backend detects Newton
133+
and falls back to standalone Newton GL capture with a logged warning.
134+
"""
144135
from isaaclab_newton.physics import MJWarpSolverCfg, NewtonCfg
145136
from isaaclab_visualizers.kit import KitVisualizerCfg
146137

@@ -158,13 +149,13 @@ def test_kit_visualizer_newton_source_records_rgb():
158149

159150

160151
def test_newton_gl_visualizer_source_records_rgb():
161-
"""NewtonGLVisualizerCfg + Newton + backend_source='visualizer' → Newton GL framebuffer capture."""
152+
"""NewtonVisualizerCfg + Newton + backend_source='visualizer' → Newton GL framebuffer capture."""
162153
from isaaclab_newton.physics import MJWarpSolverCfg, NewtonCfg
163-
from isaaclab_visualizers.newton import NewtonGLVisualizerCfg
154+
from isaaclab_visualizers.newton import NewtonVisualizerCfg
164155

165156
env_cfg = _cartpole_cfg(backend_source="visualizer")
166157
env_cfg.sim.physics = NewtonCfg(solver_cfg=MJWarpSolverCfg())
167-
env_cfg.sim.visualizer_cfgs = [NewtonGLVisualizerCfg(window_width=_W, window_height=_H)]
158+
env_cfg.sim.visualizer_cfgs = [NewtonVisualizerCfg(window_width=_W, window_height=_H)]
168159

169160
frame = _capture_frame(env_cfg)
170161
_assert_valid_frame(frame, "newton-gl-visualizer-source")

0 commit comments

Comments
 (0)