Skip to content

Commit b422e5a

Browse files
committed
Fix Newton viewer fully-black: assign PyVec3 to camera.pos
NewtonVisualizer._apply_camera_pose was assigning ``self._viewer.camera.pos = wp.vec3(*cam_pos)``, but Newton's ``Camera.translate()`` adds a ``pyglet.math.Vec3`` delta to ``camera.pos`` via ``+=`` on every viewer update. warp 1.13's strict ``__add__`` rejects ``wp.vec3 + pyglet.math.Vec3`` with:: TypeError: Built-in functions cannot be called with non-Warp array types, such as lists, tuples, and NumPy arrays. Use a Warp type such as `wp.vec`, `wp.mat`, `wp.quat`, or `wp.transform`. The TypeError is silenced by the visualizer's ``try/except`` (``logger.debug``), which then short-circuits before ``renderer.render()`` -- so the framebuffer never gets written and ``ViewerGL.get_frame`` reads back all zeros. The test asserts that the last frame is non-black, hence "Viewer frame appears fully black." Same render-path failure manifested across the Newton 1.2.0rc2 + warp 1.13 cohort. Earlier hypotheses ruled out: viewer-code rc1->rc2 diff, ``wp.RegisteredGLBuffer`` API change, pure flakiness, the bump cohort alone, ``_make_current()``, and an explicit ``glFinish`` + ``wp.synchronize_device``. Direct CPU readback of the FBO confirmed it was empty (and a control ``glClear`` to red persisted untouched across all 60 frames -- proof that nothing was writing to the FBO, not even the renderer's own ``glClearColor`` + ``glClear``). Local repro on a non-MIG L40 with Kit 110.0.0 + Newton 1.2.0rc2 + warp 1.13.0 reproduces the failure deterministically. With ``cam_pos`` assigned as ``pyglet.math.Vec3`` instead, the ``+=`` is type-homogeneous, no exception, ``renderer.render()`` runs, and both ``[physx]`` and ``[newton]`` parametrizations pass in ~50 s each. This also re-enables the test that was skipped as a workaround in PR #5538.
1 parent e15b1d0 commit b422e5a

3 files changed

Lines changed: 26 additions & 17 deletions

File tree

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,15 @@
1+
Fixed
2+
^^^^^
3+
4+
* Fixed ``test_visualizer_cartpole_integration::test_cartpole_newton_visualizer_viewergl_rgb_motion``
5+
returning a fully-black ``ViewerGL.get_frame`` buffer on the Newton 1.2.0rc2
6+
+ warp 1.13 cohort. ``NewtonVisualizer._apply_camera_pose`` was assigning
7+
``self._viewer.camera.pos = wp.vec3(*cam_pos)``, but Newton's
8+
``Camera.translate()`` adds a ``pyglet.math.Vec3`` delta with ``+=``.
9+
warp 1.13's strict ``__add__`` rejects ``wp.vec3 + pyglet.math.Vec3``
10+
with ``TypeError``; the exception was silenced by the visualizer's
11+
``try/except``, which prevented ``renderer.render()`` from ever running
12+
-- so the framebuffer stayed empty and read back as all zeros. The fix
13+
assigns ``pyglet.math.Vec3`` instead, matching what Newton uses internally.
14+
* Re-enabled ``test_cartpole_newton_visualizer_viewergl_rgb_motion`` after the
15+
workaround skip in https://github.com/isaac-sim/IsaacLab/pull/5538.

source/isaaclab_visualizers/isaaclab_visualizers/newton/newton_visualizer.py

Lines changed: 11 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -463,7 +463,17 @@ def _apply_camera_pose(self, pose: tuple[tuple[float, float, float], tuple[float
463463
if self._viewer is None:
464464
return
465465
cam_pos, cam_target = pose
466-
self._viewer.camera.pos = wp.vec3(*cam_pos)
466+
# Newton's ``Camera.translate()`` adds a ``pyglet.math.Vec3`` delta to
467+
# ``camera.pos`` via ``+=`` every viewer update. warp 1.13's strict
468+
# ``__add__`` rejects ``wp.vec3 + pyglet.math.Vec3`` with
469+
# ``TypeError: Built-in functions cannot be called with non-Warp array
470+
# types``. The exception is silenced by the visualizer's try/except,
471+
# which then skips ``renderer.render()`` -- the FBO is never written
472+
# and the frame reads back fully black. Assigning a ``pyglet.math.Vec3``
473+
# here keeps the add homogeneous.
474+
from pyglet.math import Vec3 as _PyVec3
475+
476+
self._viewer.camera.pos = _PyVec3(float(cam_pos[0]), float(cam_pos[1]), float(cam_pos[2]))
467477
cam_pos_np = np.array(cam_pos, dtype=np.float32)
468478
cam_target_np = np.array(cam_target, dtype=np.float32)
469479
direction = cam_target_np - cam_pos_np

source/isaaclab_visualizers/test/test_visualizer_cartpole_integration.py

Lines changed: 0 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -522,22 +522,6 @@ def test_cartpole_newton_visualizer_tiled_camera_rgb_non_black(
522522

523523

524524
@pytest.mark.isaacsim_ci
525-
@pytest.mark.skip(
526-
reason=(
527-
"ViewerGL.get_frame returns a fully-black 600x600x3 buffer in CI on the current "
528-
"Isaac Sim image + Newton 1.2.0rc2 + warp-lang 1.13 cohort. Failure is "
529-
"deterministic across two consecutive reruns of the same SHA and reproduces on "
530-
"every PR that touches the rendering / camera / sensor / USD stack (5 PRs hit it "
531-
"in the last 100 build.yaml runs); zero failures on PRs outside that scope. "
532-
"Investigation ruled out: rc1->rc2 viewer code diff (7-line image_logger.clear "
533-
"only), wp.RegisteredGLBuffer API (byte-identical 1.12 vs 1.13), pure flakiness "
534-
"(deterministic), and the bump cohort alone (warp-1.12 branches both pass and "
535-
"fail). Strongest remaining hypothesis: a CUDA-OpenGL interop init-order "
536-
"fragility in the PBO + glReadPixels + RegisteredGLBuffer.map path that gets "
537-
"tipped by any source change perturbing GL/CUDA bring-up. Re-enable once root "
538-
"cause is identified."
539-
)
540-
)
541525
@pytest.mark.parametrize("backend_kind", ["physx", "newton"])
542526
def test_cartpole_newton_visualizer_viewergl_rgb_motion(backend_kind: str, caplog: pytest.LogCaptureFixture) -> None:
543527
"""Newton GL (``ViewerGL.get_frame``): full motion steps, last frame non-black; early vs late differ; logs."""

0 commit comments

Comments
 (0)