Skip to content

Remove direct visualizer blank prints#5627

Open
mzl2233 wants to merge 15 commits into
isaac-sim:developfrom
mzl2233:fix-visualizer-pr-5610
Open

Remove direct visualizer blank prints#5627
mzl2233 wants to merge 15 commits into
isaac-sim:developfrom
mzl2233:fix-visualizer-pr-5610

Conversation

@mzl2233
Copy link
Copy Markdown

@mzl2233 mzl2233 commented May 15, 2026

Summary

  • remove direct blank-line prints from Rerun and Viser visualizer startup
  • keep viewer URL output routed through the existing deferred startup message path

Validation

  • python3 -m py_compile source/isaaclab_visualizers/isaaclab_visualizers/rerun/rerun_visualizer.py source/isaaclab_visualizers/isaaclab_visualizers/viser/viser_visualizer.py

@github-actions github-actions Bot added bug Something isn't working documentation Improvements or additions to documentation isaac-lab Related to Isaac Lab team labels May 15, 2026
@greptile-apps
Copy link
Copy Markdown
Contributor

greptile-apps Bot commented May 15, 2026

Greptile Summary

This PR removes noisy blank-line prints from the Rerun and Viser visualizer startup by routing the browser URL through a new deferred _log_viewer_url / flush_startup_messages mechanism, flushed on the first update_visualizers call. It also flips both visualizers' open_browser default from True to False, adds bind_address/display_address fields to ViserVisualizerCfg, and fixes VisualizationMarkers so the Kit USD backend is initialized when sim.is_rendering is true even with no active visualizers.

  • Deferred URL printing: _log_viewer_url queues a Unicode box frame into _deferred_startup_messages; SimulationContext.update_visualizers() flushes it once at the start of the first step, ensuring the URL appears at the right point in the startup sequence.
  • Monkey-patch approach for browser suppression: NewtonViewerRerun temporarily replaces rr.serve_web_viewer and NewtonViewerViser temporarily replaces viser.ViserServer to inject open_browser=False / host=bind_address without requiring upstream API changes.
  • verbose=False hardcoded for Viser: NewtonViewerViser always receives verbose=False, so viser's own websocket/server startup output is always suppressed regardless of cfg.verbose; the flag now only controls whether the URL box is displayed.

Confidence Score: 4/5

Safe to merge; changes are limited to startup logging and default browser-open behavior with no impact on simulation correctness.

The core simulation and training paths are untouched. The three findings are style/behavior edge cases: verbose=False hardcoded for Viser changes what the verbose flag means, the signature-inspection suppression in the Rerun browser-suppression wrapper has a silent fallback, and the module-attribute monkey-patches are not protected against concurrent initialization. None of these affect normal single-process runs.

rerun_visualizer.py and viser_visualizer.py — the monkey-patch initialization blocks and the verbose=False hardcoding are worth a second look.

Important Files Changed

Filename Overview
source/isaaclab/isaaclab/visualizers/base_visualizer.py Adds deferred URL-box printing machinery (_log_viewer_url, flush_startup_messages) and downgrades initialization table logging from INFO to DEBUG
source/isaaclab_visualizers/isaaclab_visualizers/rerun/rerun_visualizer.py Monkey-patches rr.serve_web_viewer to suppress browser open when open_browser=False; changes URL encoding to safe=':/' for readability; defers URL box via base class
source/isaaclab_visualizers/isaaclab_visualizers/viser/viser_visualizer.py Removes _suppress_viser_startup_logs; always passes verbose=False to NewtonViewerViser; adds bind_address forwarding and deferred URL box; share_url property used for URL fallback
source/isaaclab/isaaclab/sim/simulation_context.py Adds flush_startup_messages() loop at the top of update_visualizers() so deferred URL boxes are printed on the first step call
source/isaaclab/isaaclab/markers/visualization_markers.py Fixes _ensure_backends_initialized to check sim.is_rendering so USD markers work when no visualizer is active but rendering is enabled
source/isaaclab_visualizers/isaaclab_visualizers/rerun/rerun_visualizer_cfg.py open_browser default changed from True to False; docstring updated to reflect URL-always-logged behavior
source/isaaclab_visualizers/isaaclab_visualizers/viser/viser_visualizer_cfg.py Adds bind_address and display_address fields; open_browser default changed from True to False
source/isaaclab/test/sim/test_simulation_context_visualizers.py Adds tests for open_browser opt-in, bind_address forwarding, and deferred URL printing; updates fake visualizers with flush_startup_messages stub
source/isaaclab/test/markers/test_visualization_markers.py Adds unit tests for the is_rendering-gated kit backend initialization path
source/isaaclab_visualizers/test/test_visualizer_cartpole_integration.py Expands Newton/Kit integration tests with simulation-pause and render-pause verification; reduces test step count from 60 to 10; lazily imports Rerun/Viser classes

Sequence Diagram

sequenceDiagram
    participant Cfg as RerunVisualizerCfg / ViserVisualizerCfg
    participant Viz as RerunVisualizer / ViserVisualizer
    participant Base as BaseVisualizer
    participant SC as SimulationContext

    Cfg->>Viz: initialize(scene_data_provider)
    Viz->>Viz: create viewer (NewtonViewerRerun / NewtonViewerViser)
    Note over Viz: open_browser defaults to False
    Viz->>Base: _log_viewer_url(name, url)
    Base->>Base: append box to _deferred_startup_messages
    Viz-->>SC: (initialization complete)

    SC->>SC: update_visualizers(dt)
    loop first call per visualizer
        SC->>Base: flush_startup_messages()
        Base->>Base: print URL box to stdout
        Base->>Base: _deferred_startup_messages.clear()
    end
    SC->>Viz: viz.step(dt)

    SC->>SC: update_visualizers(dt) [subsequent calls]
    SC->>Base: flush_startup_messages()
    Note over Base: list is empty — no-op
    SC->>Viz: viz.step(dt)
Loading

Reviews (1): Last reviewed commit: "Remove direct visualizer blank prints" | Re-trigger Greptile

Comment on lines +272 to +280
self._viewer = NewtonViewerViser(
port=self.cfg.port,
bind_address=self.cfg.bind_address,
label=self.cfg.label,
verbose=False,
share=self.cfg.share,
record_to_viser=record_to_viser,
metadata=metadata or {},
)
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 verbose=False is hardcoded here, so cfg.verbose=True no longer exposes viser's internal startup logs — it only controls whether the URL box is printed. Previously _suppress_viser_startup_logs(enabled=not self.cfg.verbose) honoured the config for both. Any user who sets verbose=True to get raw websocket/server startup output for debugging will see only the URL box and nothing else.

Suggested change
self._viewer = NewtonViewerViser(
port=self.cfg.port,
bind_address=self.cfg.bind_address,
label=self.cfg.label,
verbose=False,
share=self.cfg.share,
record_to_viser=record_to_viser,
metadata=metadata or {},
)
self._viewer = NewtonViewerViser(
port=self.cfg.port,
bind_address=self.cfg.bind_address,
label=self.cfg.label,
verbose=self.cfg.verbose,
share=self.cfg.share,
record_to_viser=record_to_viser,
metadata=metadata or {},
)

Comment on lines +104 to +109
def _serve_web_viewer_without_browser(*serve_args, **serve_kwargs):
with contextlib.suppress(TypeError, ValueError):
supports_open_browser = "open_browser" in inspect.signature(original_serve_web_viewer).parameters
if supports_open_browser:
serve_kwargs.setdefault("open_browser", False)
return original_serve_web_viewer(*serve_args, **serve_kwargs)
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 Silent inspection failure may still open browser

If inspect.signature(original_serve_web_viewer) raises TypeError or ValueError (e.g., a C-extension binding in a future rerun release), the entire with contextlib.suppress(...) block exits silently without setting open_browser=False in serve_kwargs. The original rr.serve_web_viewer is then called with unmodified kwargs, potentially opening the browser even when open_browser=False was configured.

Comment on lines +111 to +114
with contextlib.ExitStack() as stack:
rr.serve_web_viewer = _serve_web_viewer_without_browser
stack.callback(setattr, rr, "serve_web_viewer", original_serve_web_viewer)
super().__init__(*args, **kwargs)
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 Module-level attribute swap is not thread-safe

rr.serve_web_viewer is replaced on the rr module for the duration of super().__init__. If two NewtonViewerRerun instances are created concurrently (e.g., in a multi-process or multi-threaded test harness), thread B may capture the already-patched wrapper as its original_serve_web_viewer, and after thread A restores the real function, thread B would write the patched wrapper back. The same pattern applies to the viser.ViserServer swap in NewtonViewerViser.__init__. A threading lock around the swap-and-restore block would eliminate the race.

Copy link
Copy Markdown

@isaaclab-review-bot isaaclab-review-bot Bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Code Review: PR #5627 — Remove direct visualizer blank prints

Thank you for this cleanup PR! The changes improve user experience by deferring visualizer URL display until the simulation loop starts, preventing noise during initialization. The implementation is solid overall, with good test coverage for the new functionality.

🔬 Design Assessment

Architecture fit: The deferred startup message pattern is well-designed. Queuing messages in _log_viewer_url() and flushing via flush_startup_messages() in update_visualizers() ensures URLs appear at the right time (just before the simulation loop) rather than during potentially noisy initialization.

Cross-module impact: Self-contained. The changes affect only the visualizer subsystem and don't break any existing interfaces.

🔵 Suggestions

1. flush_startup_messages() called on every framesource/isaaclab/isaaclab/sim/simulation_context.py:764

The flush_startup_messages() method is called on every update_visualizers() invocation. While the implementation clears the list after printing (making subsequent calls no-ops), iterating over all visualizers and calling this method every frame is unnecessary overhead for a one-time operation.

Consider tracking whether startup messages have been flushed to avoid repeated calls:

# In SimulationContext
if not self._visualizers:
    return

if not getattr(self, '_startup_messages_flushed', False):
    for viz in self._visualizers:
        viz.flush_startup_messages()
    self._startup_messages_flushed = True

Alternatively, flush only once per visualizer by adding a flag in BaseVisualizer.

2. verbose parameter disconnect in NewtonViewerVisersource/isaaclab_visualizers/isaaclab_visualizers/viser/viser_visualizer.py:272

NewtonViewerViser.__init__ accepts a verbose parameter, but ViserVisualizer._create_viewer() always passes verbose=False. This means the verbose parameter in NewtonViewerViser has no effect at runtime. Consider either:

  • Removing the verbose parameter from NewtonViewerViser since it's always False
  • Or passing self.cfg.verbose to allow users to control internal viser logging

This is minor since the current behavior (suppress internal logs, use cfg.verbose for Isaac Lab URL logging) appears intentional.

🧪 Test Coverage

The PR includes good test coverage:

  • test_rendering_without_visualizers_initializes_kit_backend — verifies rendering mode still creates Kit backend
  • test_non_rendering_without_visualizers_defers_backend_initialization — verifies no backend when not needed
  • test_kit_visualizer_initializes_kit_backend_when_rendering_flag_is_false — verifies visualizer overrides rendering flag
  • test_rendering_context_authors_visible_usd_point_instancer — verifies USD markers work with rendering
  • test_viser_visualizer_open_browser_is_opt_in — verifies open_browser=False default
  • test_rerun_visualizer_open_browser_is_opt_in — verifies open_browser=False default
  • test_web_visualizer_cfgs_do_not_open_browser_by_default — verifies config defaults

The deferred message flushing is exercised through integration tests. Consider adding a unit test specifically for _log_viewer_url() URL formatting with edge cases (e.g., URLs without ports).

✅ Summary

Verdict: Ship it

The changes are well-implemented with appropriate test coverage. The suggestions above are minor improvements that could be addressed in follow-up work. The fix for visualization marker backend initialization (sim.is_rendering check) is a good catch that ensures USD markers work during camera rendering even without explicit visualizers.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

bug Something isn't working documentation Improvements or additions to documentation isaac-lab Related to Isaac Lab team

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants