You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
{{ message }}
Repository navigation
[viewer] Importing viewer module on macos immediately kills maplotlib windows #377
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:
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.
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.
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
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.
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.
Activity
saran-t commentedon Jan 27, 2023
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 factglfw.init()that's causing the problem then it's likely an incompatibility between glfw and the particular matplotlib backend.ShravanTata commentedon Jan 27, 2023
Yes, it is definitely a problem particularly with the
MacOSXbackend which happens to be the default. When I switch to, for example Qt backend (to permanently do it add the following to your (bash)rcexport MPLBACKEND=QtAgg) the issue goes awaysaran-t commentedon Jan 27, 2023
Are you getting any error message or does it just silently close?
ShravanTata commentedon Jan 27, 2023
No errors/warnings are raised. It just silently closes
akushonkamen commentedon Oct 17, 2026
I'd like to take this one. Root cause:
dm_control/_render/glfw_renderer.py:28callsglfw.init()at module import time. Becausedm_control/_render/__init__.pyprobes backends by importing them (GLFW first whenMUJOCO_GLis unset), anyimport dm_control.viewer(ordm_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) thatimport dm_control.viewertriggers oneglfw.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 aGLFWContextis actually created, plus a regression test. Disclosure: I'm assisted by an automated coding agent for this fix.akushonkamen commentedon Oct 17, 2026
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):
import dm_control.viewertriggers exactly oneglfw.init()call at import time (via_render/__init__.pyprobing GLFW first), before any rendering is requested.sys.platform == 'darwin'intoGLFWContext.__init__(dm_control/_render/glfw_renderer.py); all other platforms keep the import-time init so headless EGL/OSMesa fallback indm_control/_render/__init__.pyis unchanged.glfwInitis a no-op when already initialized, so the extra call at context creation is safe. After the patch,import dm_control.viewerperforms zeroglfw.init()calls, and real on-screen rendering still works (verified with aphysics.render()smoke test on the main thread).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 + stubbedglfwmodule, forcedsys.platform, so they run on any OS): import must perform no init on macOS and exactly one on other platforms, and creating aGLFWContextmust trigger initialization. They fail on unpatched main and pass with the patch. The rest ofdm_control/_render(base, executor, mocked GLFW context tests) stays green.akushonkamen commentedon Oct 17, 2026
Patch submitted as PR #570.