Skip to content

fix(render): defer glfw.init() on macOS until a GLFWContext is created - #570

Open
akushonkamen wants to merge 1 commit into
google-deepmind:mainfrom
akushonkamen:fix-377-defer-glfw-init-on-macos
Open

akushonkamen wants to merge 1 commit into
google-deepmind:mainfrom
akushonkamen:fix-377-defer-glfw-init-on-macos

Conversation

@akushonkamen

Copy link
Copy Markdown

Fixes #377

Summary

import dm_control._render.glfw_renderer (reachable via import dm_control.viewer or dm_control.render) currently calls glfw.init() at module import time. On macOS this disrupts the shared NSApplication state used by other GUI toolkits — most notably matplotlib's default MacOSX backend (#377). This PR defers GLFW initialization on macOS until a GLFWContext is actually created.

Root cause

dm_control/_render/glfw_renderer.py used to run glfw.init() at import time (guarded by a try/except that converts GLFWError into ImportError). Because dm_control/_render/__init__.py imports the GLFW backend as part of its automatic backend probing, any import dm_control.viewer or import dm_control.render triggered GLFW initialization, even when no GLFW rendering was ever requested. Initializing GLFW attaches to the process-wide NSApplication on macOS, which interferes with other GUI event loops sharing that state (#377).

Fix

  • Extract the initialization into _init_glfw() (same GLFWError → ImportError behavior).
  • On non-darwin platforms, keep calling it at import time, so the automatic backend selection in dm_control/_render/__init__.py can still fall back to EGL/OSMesa when GLFW cannot initialize (e.g. headless Linux) — behavior unchanged there.
  • On macOS, call _init_glfw() inside GLFWContext.__init__ instead. glfwInit is a no-op when GLFW is already initialized, so the extra call at context creation is safe.

Testing

Red → green (macOS arm64, darwin 24.6, Python 3.12, dm_control 1.0.49, mujoco 3.16.0):

$ timeout 300 .venv/bin/python dm_control/_render/glfw_renderer_test.py GlfwInitAtImportTest
# with fix stashed:  FAILED (failures=2)
# with fix applied:  Ran 2 tests in ...s   OK

Additional checks, all passing with the fix applied:

  • dm_control/_render/glfw_renderer_test.py: GLFWContextTest.test_init / test_make_current / test_freeing + GlfwInitAtImportTest — 5 tests OK
  • dm_control/_render/base_test.py — 7 tests OK
  • dm_control/_render/executor/render_executor_test.py — 30 tests OK (skipped=8)
  • dm_control/viewer test files: application_test.py, runtime_test.py, user_input_test.py, util_test.py OK; renderer_test.py OK (skipped=40); views_test.py OK (skipped=1)
  • python -m py_compile on both changed files — OK
  • Real render smoke test (physics.render() on the main thread with MUJOCO_GL=glfw) — succeeds both before and after the fix
  • Mechanism verification: with a stub glfw module counting init() calls, import dm_control.viewer calls glfw.init() 1× before the fix and 0× after; GLFWContext(...) still initializes exactly once.

Honest limitations:

AI disclosure

This change was prepared with the assistance of an AI coding agent (GLM, via the Contributor automation workflow); the GitHub account owner reviewed, tested, and is submitting the contribution under the project CLA.

Importing 'dm_control._render.glfw_renderer' (reachable via
'import dm_control.viewer' or 'dm_control.render') called 'glfw.init()' at
module import time. On macOS this disrupts the shared NSApplication state
used by other GUI toolkits, most notably matplotlib's default 'MacOSX'
backend (google-deepmind#377).

Keep the import-time initialization on all other platforms so that the
automatic backend selection in 'dm_control/_render/__init__.py' can still
fall back to EGL/OSMesa when GLFW cannot initialize (e.g. headless Linux),
and initialize inside 'GLFWContext.__init__' on macOS instead. 'glfwInit'
is a no-op when GLFW is already initialized, so the extra call is safe.
@google-cla

google-cla Bot commented Oct 17, 2026

Copy link
Copy Markdown

Thanks for your pull request! It looks like this may be your first contribution to a Google open source project. Before we can look at your pull request, you'll need to sign a Contributor License Agreement (CLA).

View this failed invocation of the CLA check for more information.

For the most up to date status, view the checks section at the bottom of the pull request.

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

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

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

1 participant