Skip to content

[viewer] Importing viewer module on macos immediately kills maplotlib windows #377

Open

Description

@ShravanTata

On MacOS, importing the viewer module from dm_control immediately kills the matplotlib plot figures even when it should be ideally blocked with plt.show(). To reproduce the issue run the code below as a script:

import matplotlib
import matplotlib.pyplot as plt

from dm_control import viewer

if __name__ == '__main__':
    print(f"Matplotlib backed {matplotlib.get_backend()} ")
    plt.plot()
    plt.show(block=True)

NOTE : To reproduce the bug from the above script make sure that backend used by matplotlib (printed on the terminal) is MacOSX. When using other non default backends on MacOS the issue goes away.

Looking into it I found that the issue comes from https://github.com/deepmind/dm_control/blob/3c67a42d021b97808fcc075c0c468d78f0c2233f/dm_control/_render/glfw_renderer.py#L28 Seems like having glfw.init() run globally is the cause.

Activity

  1. saran-t commented on Jan 27, 2023

    @saran-t
    Member

    Haven't had a chance to actually look into this yet, but regarding glfw.init(), that's the only way to run it. There isn't a "run it locally" option. If it is in fact glfw.init() that's causing the problem then it's likely an incompatibility between glfw and the particular matplotlib backend.

  2. ShravanTata commented on Jan 27, 2023

    @ShravanTata
    Author

    Yes, it is definitely a problem particularly with the MacOSX backend which happens to be the default. When I switch to, for example Qt backend (to permanently do it add the following to your (bash)rc export MPLBACKEND=QtAgg) the issue goes away

  3. saran-t commented on Jan 27, 2023

    @saran-t
    Member

    Are you getting any error message or does it just silently close?

  4. ShravanTata commented on Jan 27, 2023

    @ShravanTata
    Author

    No errors/warnings are raised. It just silently closes

  5. akushonkamen commented on Oct 17, 2026

    @akushonkamen

    I'd like to take this one. Root cause: dm_control/_render/glfw_renderer.py:28 calls glfw.init() at module import time. Because dm_control/_render/__init__.py probes backends by importing them (GLFW first when MUJOCO_GL is unset), any import dm_control.viewer (or dm_control.render) initializes GLFW on macOS, which disrupts the shared NSApplication state that matplotlib's MacOSX backend depends on. I verified on macOS arm64 (dm_control 1.0.49, Python 3.12) that import dm_control.viewer triggers one glfw.init() call before any rendering is requested.

    Plan: keep the import-time init on non-macOS platforms (so headless backend fallback probing is unchanged), and on macOS defer glfw.init() until a GLFWContext is actually created, plus a regression test. Disclosure: I'm assisted by an automated coding agent for this fix.

  6. akushonkamen commented on Oct 17, 2026

    @akushonkamen

    Status: I have a patch ready (will follow as a PR).

    What I verified on macOS arm64 (darwin 24.6, Python 3.12, dm_control 1.0.49, mujoco 3.16.0):

    • Mechanism confirmed: import dm_control.viewer triggers exactly one glfw.init() call at import time (via _render/__init__.py probing GLFW first), before any rendering is requested.
    • The fix defers initialization on sys.platform == 'darwin' into GLFWContext.__init__ (dm_control/_render/glfw_renderer.py); all other platforms keep the import-time init so headless EGL/OSMesa fallback in dm_control/_render/__init__.py is unchanged. glfwInit is a no-op when already initialized, so the extra call at context creation is safe. After the patch, import dm_control.viewer performs zero glfw.init() calls, and real on-screen rendering still works (verified with a physics.render() smoke test on the main thread).
    • To be transparent about scope: the originally reported symptom (MacOSX-backend matplotlib windows silently closing) no longer reproduces for me with current dependency versions, in either ordering (viewer imported before plt.show() or after the window is shown; the window stays alive in both cases). So this patch removes the import-time global side effect reported here rather than fixing an acutely reproducible crash on current software.

    Tests added in dm_control/_render/glfw_renderer_test.py (subprocess + stubbed glfw module, forced sys.platform, so they run on any OS): import must perform no init on macOS and exactly one on other platforms, and creating a GLFWContext must trigger initialization. They fail on unpatched main and pass with the patch. The rest of dm_control/_render (base, executor, mocked GLFW context tests) stays green.

  7. akushonkamen commented on Oct 17, 2026

    @akushonkamen

    Patch submitted as PR #570.

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

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions