Skip to content

Commit d60ee5a

Browse files
authored
Initialize visualizers before timeline play on reset (#6176)
## Summary - Initializes first-time visualizers after `physics_manager.reset()` has created PhysX tensor views, but before `physics_manager.play()` pumps Kit timeline events. - Prevents Newton visualizer initialization from seeing a PhysX scene-data backend whose tensor view was invalidated by a STOP event processed during the play event pump. - Adds a focused unit test that locks the reset ordering to `reset -> initialize_visualizers -> play` for first-time visualizers. ## Root cause The reported command selects the Newton visualizer, but not Newton physics, so the failing path is PhysX simulation feeding the Newton visualizer through the scene-data provider. `PhysxManager.reset()` creates the PhysX tensor simulation view and stores it in the scene-data backend. `SimulationContext.reset()` then called `physics_manager.play()` before creating visualizers. `PhysxManager.play()` calls `omni.kit.app.get_app().update()`, which can process timeline events. On the failing Windows/Kit path, a STOP event is processed there, and `PhysxManager._on_stop()` invalidates the freshly created tensor views. The Newton visualizer is then initialized after that invalidation and asks scene data for `transform_paths`, which reaches `RigidBodyView.prim_paths` on an invalid PhysX view and crashes with `NoneType.prim_paths`. The correct fix is to initialize visualizers before the play event pump, not to make scene-data consumers tolerate already-invalid views. ## Validation - `python3 -m py_compile source/isaaclab/isaaclab/sim/simulation_context.py source/isaaclab/test/sim/test_simulation_context_visualizers.py` - `python3 tools/changelog/cli.py check develop` - `git diff --cached --check` - `pre-commit run --files source/isaaclab/isaaclab/sim/simulation_context.py source/isaaclab/test/sim/test_simulation_context_visualizers.py source/isaaclab/changelog.d/zhengyuz-sim-reset-visualizer-before-play.rst` Focused pytest was attempted locally, but this shell lacks runtime deps (`lazy_loader`) needed to import the visualizer test module.
1 parent 9103ad2 commit d60ee5a

3 files changed

Lines changed: 36 additions & 3 deletions

File tree

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,4 @@
1+
Fixed
2+
^^^^^
3+
4+
* Fixed SimulationContext reset ordering so initial visualizers are created before the timeline play event pump can invalidate freshly created PhysX tensor views.

source/isaaclab/isaaclab/sim/simulation_context.py

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -724,11 +724,11 @@ def reset(self, soft: bool = False) -> None:
724724
self.physics_manager.reset(soft)
725725
for viz in self._visualizers:
726726
viz.reset(soft)
727-
# Start the timeline so the play button is pressed
728-
self.physics_manager.play()
729727
if not self._visualizers:
730-
# Initialize visualizers after PhysX sim view is ready.
728+
# Initialize visualizers after PhysX sim views are ready, but before play() pumps timeline events.
731729
self.initialize_visualizers()
730+
# Start the timeline so the play button is pressed
731+
self.physics_manager.play()
732732
self._is_playing = True
733733
self._is_stopped = False
734734

source/isaaclab/test/sim/test_simulation_context_visualizers.py

Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -194,6 +194,35 @@ def test_update_visualizers_handles_training_pause_loop():
194194
assert viz.step_calls == [0.0, 0.2]
195195

196196

197+
def test_reset_initializes_visualizers_before_playing_timeline():
198+
"""Initial visualizers must see the PhysX views created by reset before play() pumps timeline events."""
199+
events: list[str] = []
200+
ctx = object.__new__(SimulationContext)
201+
ctx._visualizers = []
202+
203+
class _PhysicsManager:
204+
@staticmethod
205+
def reset(soft=False):
206+
events.append(f"reset:{soft}")
207+
208+
@staticmethod
209+
def play():
210+
events.append("play")
211+
212+
def _initialize_visualizers():
213+
events.append("initialize_visualizers")
214+
ctx._visualizers = [_FakeVisualizer()]
215+
216+
ctx.physics_manager = _PhysicsManager()
217+
ctx.initialize_visualizers = _initialize_visualizers
218+
219+
ctx.reset()
220+
221+
assert events == ["reset:False", "initialize_visualizers", "play"]
222+
assert ctx.is_playing()
223+
assert not ctx.is_stopped()
224+
225+
197226
class _DummyViserSceneDataProvider:
198227
@property
199228
def num_envs(self) -> int:

0 commit comments

Comments
 (0)