From ef8e6802eff569650d31071b8c874634a71b1f94 Mon Sep 17 00:00:00 2001 From: Haroon Qureshi Date: Tue, 3 Feb 2026 08:11:46 -0800 Subject: [PATCH] Move EGL initialization into a separate class. Initialize the EGL context in the Renderer when using the classic rendering engine with no window. PiperOrigin-RevId: 864884976 Change-Id: Ic6cc4637855a361e41f3bfb4b738c012ec93711f --- src/experimental/platform/CMakeLists.txt | 2 + src/experimental/platform/egl_utils.cc | 144 +++++++++++++++++++++++ src/experimental/platform/egl_utils.h | 28 +++++ src/experimental/platform/renderer.cc | 10 +- src/experimental/platform/renderer.h | 3 + src/experimental/platform/window.cc | 99 ---------------- src/experimental/platform/window.h | 8 -- 7 files changed, 185 insertions(+), 109 deletions(-) create mode 100644 src/experimental/platform/egl_utils.cc create mode 100644 src/experimental/platform/egl_utils.h diff --git a/src/experimental/platform/CMakeLists.txt b/src/experimental/platform/CMakeLists.txt index 80035ff0..197a8b5e 100644 --- a/src/experimental/platform/CMakeLists.txt +++ b/src/experimental/platform/CMakeLists.txt @@ -37,6 +37,8 @@ target_compile_definitions(${MUJOCO_PLATFORM_TARGET_NAME} target_sources(${MUJOCO_PLATFORM_TARGET_NAME} PUBLIC + egl_utils.cc + egl_utils.h file_dialog.h gui.cc gui.h diff --git a/src/experimental/platform/egl_utils.cc b/src/experimental/platform/egl_utils.cc new file mode 100644 index 00000000..ed122012 --- /dev/null +++ b/src/experimental/platform/egl_utils.cc @@ -0,0 +1,144 @@ +// Copyright 2026 DeepMind Technologies Limited +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#include "experimental/platform/egl_utils.h" + +#include + +#include "third_party/GL/gl/include/EGL/egl.h" +#include "third_party/GL/gl/include/EGL/eglext.h" +#include + +namespace mujoco::platform { + +static EGLDisplay CreateInitializedEglDisplay() { + auto eglQueryDevicesEXT = + (PFNEGLQUERYDEVICESEXTPROC)eglGetProcAddress("eglQueryDevicesEXT"); + auto eglGetPlatformDisplayEXT = + (PFNEGLGETPLATFORMDISPLAYEXTPROC)eglGetProcAddress( + "eglGetPlatformDisplayEXT"); + + if (!eglQueryDevicesEXT || !eglGetPlatformDisplayEXT) { + mju_error("Failed to load EGL functions"); + return EGL_NO_DISPLAY; + } + + // Query all available EGL devices. + constexpr int kMaxDevices = 32; + EGLDeviceEXT devices[kMaxDevices]; + int ndevice = 0; + if (!eglQueryDevicesEXT(kMaxDevices, devices, &ndevice)) { + mju_error("eglQueryDevices error: 0x%x", eglGetError()); + return EGL_NO_DISPLAY; + } + + // Initialize the first valid EGL display. + for (int i = 0; i < ndevice; ++i) { + EGLDisplay display = + eglGetPlatformDisplayEXT(EGL_PLATFORM_DEVICE_EXT, devices[i], nullptr); + if (display != EGL_NO_DISPLAY) { + int major, minor; + EGLBoolean initialized = eglInitialize(display, &major, &minor); + if (initialized) { + return display; + } + } + } + mju_error("Failed to create and initialize a valid EGL display!"); + return EGL_NO_DISPLAY; +} + +static EGLContext InitializeEglContext(EGLDisplay display) { + constexpr EGLint config_attribs[] = {EGL_RED_SIZE, + 8, + EGL_GREEN_SIZE, + 8, + EGL_BLUE_SIZE, + 8, + EGL_ALPHA_SIZE, + 8, + EGL_DEPTH_SIZE, + 24, + EGL_STENCIL_SIZE, + 8, + EGL_COLOR_BUFFER_TYPE, + EGL_RGB_BUFFER, + EGL_SURFACE_TYPE, + EGL_PBUFFER_BIT, + EGL_RENDERABLE_TYPE, + EGL_OPENGL_BIT, + EGL_NONE}; + EGLint nconfig; + EGLConfig config; + if (!eglChooseConfig(display, config_attribs, &config, 1, &nconfig)) { + mju_error("eglChooseConfig error: 0x%x", eglGetError()); + } + + // Bind the OpenGL API to the EGL. + if (!eglBindAPI(EGL_OPENGL_API)) { + mju_error("eglBindAPI error: 0x%x", eglGetError()); + } + + // Create an EGL context. + constexpr EGLint context_attribs[] = { + EGL_CONTEXT_MAJOR_VERSION, + 1, + EGL_CONTEXT_MINOR_VERSION, + 5, + EGL_CONTEXT_OPENGL_PROFILE_MASK, + EGL_CONTEXT_OPENGL_COMPATIBILITY_PROFILE_BIT, + EGL_NONE}; + EGLContext context = + eglCreateContext(display, config, EGL_NO_CONTEXT, context_attribs); + if (context == EGL_NO_CONTEXT) { + mju_error("eglCreateContext error: 0x%x", eglGetError()); + return EGL_NO_CONTEXT; + } + + // Make the EGL context current. + if (!eglMakeCurrent(display, nullptr, nullptr, context)) { + mju_error("eglMakeCurrent error: 0x%x", eglGetError()); + return EGL_NO_CONTEXT; + } + return context; +} + +struct EglContext { + EglContext() { + display_ = CreateInitializedEglDisplay(); + context_ = InitializeEglContext(display_); + } + + ~EglContext() { + if (context_ != EGL_NO_CONTEXT) { + eglMakeCurrent(display_, nullptr, nullptr, EGL_NO_CONTEXT); + eglDestroyContext(display_, context_); + context_ = EGL_NO_CONTEXT; + } + if (display_ != EGL_NO_DISPLAY) { + eglTerminate(display_); + display_ = EGL_NO_DISPLAY; + } + } + + EGLDisplay display_; + EGLContext context_; +}; + +std::shared_ptr CreateEglContext() { + auto egl_context = std::make_shared(); + return std::static_pointer_cast(egl_context); +} + +} // namespace mujoco::platform diff --git a/src/experimental/platform/egl_utils.h b/src/experimental/platform/egl_utils.h new file mode 100644 index 00000000..dc01dfda --- /dev/null +++ b/src/experimental/platform/egl_utils.h @@ -0,0 +1,28 @@ +// Copyright 2026 DeepMind Technologies Limited +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#ifndef MUJOCO_SRC_EXPERIMENTAL_PLATFORM_EGL_UTILS_H_ +#define MUJOCO_SRC_EXPERIMENTAL_PLATFORM_EGL_UTILS_H_ + +#include + +namespace mujoco::platform { + +// Creates an opaque handle to an initialized EGL context and display. +// Resetting this handle will destroy the context and release the display. +std::shared_ptr CreateEglContext(); + +} // namespace mujoco::platform + +#endif // MUJOCO_SRC_EXPERIMENTAL_PLATFORM_EGL_UTILS_H_ diff --git a/src/experimental/platform/renderer.cc b/src/experimental/platform/renderer.cc index bff07e83..d6f19ea5 100644 --- a/src/experimental/platform/renderer.cc +++ b/src/experimental/platform/renderer.cc @@ -23,6 +23,7 @@ #if defined(MUJOCO_RENDERER_CLASSIC_OPENGL) #include #include +#include "experimental/platform/egl_utils.h" #else #include "experimental/filament/render_context_filament.h" #include "experimental/platform/plugin.h" @@ -32,11 +33,17 @@ namespace mujoco::platform { Renderer::Renderer(void* native_window) : native_window_(native_window) { #ifdef MUJOCO_RENDERER_CLASSIC_OPENGL + if (native_window == nullptr) { + graphics_api_context_ = CreateEglContext(); + } ImGui_ImplOpenGL3_Init(); #endif } -Renderer::~Renderer() { Deinit(); } +Renderer::~Renderer() { + Deinit(); + graphics_api_context_.reset(); +} void Renderer::Init(const mjModel* model) { Deinit(); @@ -184,7 +191,6 @@ RendererBackend Renderer::GetBackend() { #error "Unsupported renderer backend." #endif } - } // namespace mujoco::platform #if !defined(MUJOCO_RENDERER_CLASSIC_OPENGL) diff --git a/src/experimental/platform/renderer.h b/src/experimental/platform/renderer.h index 05091c0a..dca5369f 100644 --- a/src/experimental/platform/renderer.h +++ b/src/experimental/platform/renderer.h @@ -17,12 +17,14 @@ #include #include +#include #include #include #include #include "experimental/platform/renderer_backend.h" + namespace mujoco::platform { // Renders the mujoco simulation and the imgui state. @@ -99,6 +101,7 @@ class Renderer { void UpdateFps(); void* native_window_ = nullptr; + std::shared_ptr graphics_api_context_ = nullptr; mjrContext render_context_; mjvScene scene_; bool initialized_ = false; diff --git a/src/experimental/platform/window.cc b/src/experimental/platform/window.cc index 823b7a68..686ac36c 100644 --- a/src/experimental/platform/window.cc +++ b/src/experimental/platform/window.cc @@ -20,10 +20,6 @@ #include #include -#ifdef MUJOCO_STUDIO_EGL_SUPPORTED -#include "third_party/GL/gl/include/EGL/egl.h" -#include "third_party/GL/gl/include/EGL/eglext.h" -#endif #include #include #include @@ -160,9 +156,6 @@ Window::Window(std::string_view title, int width, int height, Config config) if (config_.offscreen_mode) { sdl_renderer_ = SDL_CreateRenderer(sdl_window_, -1, SDL_RENDERER_SOFTWARE); - if (renderer_backend == RendererBackend::ClassicOpenGl) { - InitOffscreenEglContext(); - } } SDL_SysWMinfo wmi; @@ -187,13 +180,6 @@ Window::Window(std::string_view title, int width, int height, Config config) } Window::~Window() { -#ifdef MUJOCO_STUDIO_EGL_SUPPORTED - if (egl_display_ && egl_context_) { - eglMakeCurrent(egl_display_, nullptr, nullptr, EGL_NO_CONTEXT); - eglDestroyContext(egl_display_, egl_context_); - eglTerminate(egl_display_); - } -#endif SDL_DestroyWindow(sdl_window_); SDL_Quit(); } @@ -296,89 +282,4 @@ void Window::Present(std::span pixels) { bool Window::IsOffscreenMode() const { return config_.offscreen_mode; } - -void Window::InitOffscreenEglContext() { -#ifdef MUJOCO_STUDIO_EGL_SUPPORTED - auto eglQueryDevicesEXT = - (PFNEGLQUERYDEVICESEXTPROC)eglGetProcAddress("eglQueryDevicesEXT"); - auto eglGetPlatformDisplayEXT = - (PFNEGLGETPLATFORMDISPLAYEXTPROC)eglGetProcAddress( - "eglGetPlatformDisplayEXT"); - - if (!eglQueryDevicesEXT || !eglGetPlatformDisplayEXT) { - mju_error("Failed to load EGL functions"); - } - - // Query all available EGL devices. - constexpr int kMaxDevices = 32; - EGLDeviceEXT egl_devices[kMaxDevices]; - int ndevice = 0; - if (!eglQueryDevicesEXT(kMaxDevices, egl_devices, &ndevice)) { - mju_error("eglQueryDevices error: 0x%x", eglGetError()); - } - - // Initialize the first valid EGL display. - for (int i = 0; i < ndevice; ++i) { - egl_display_ = eglGetPlatformDisplayEXT(EGL_PLATFORM_DEVICE_EXT, - egl_devices[i], nullptr); - if (egl_display_ != EGL_NO_DISPLAY) { - int major, minor; - EGLBoolean initialized = eglInitialize(egl_display_, &major, &minor); - if (!initialized) { - egl_display_ = EGL_NO_DISPLAY; - } else { - break; - } - } - } - if (egl_display_ == EGL_NO_DISPLAY) { - mju_error("Failed to create and initialize a valid EGL display!"); - } - - // Choose an EGL config. - constexpr EGLint config_attribs[] = { - EGL_RED_SIZE, 8, - EGL_GREEN_SIZE, 8, - EGL_BLUE_SIZE, 8, - EGL_ALPHA_SIZE, 8, - EGL_DEPTH_SIZE, 24, - EGL_STENCIL_SIZE, 8, - EGL_COLOR_BUFFER_TYPE, EGL_RGB_BUFFER, - EGL_SURFACE_TYPE, EGL_PBUFFER_BIT, - EGL_RENDERABLE_TYPE, EGL_OPENGL_BIT, - EGL_NONE - }; - EGLint nconfig; - EGLConfig config; - if (!eglChooseConfig(egl_display_, config_attribs, &config, 1, &nconfig)) { - mju_error("eglChooseConfig error: 0x%x", eglGetError()); - } - - // Bind the OpenGL API to the EGL. - if (!eglBindAPI(EGL_OPENGL_API)) { - mju_error("eglBindAPI error: 0x%x", eglGetError()); - } - - // Create an EGL context. - constexpr EGLint context_attribs[] = { - EGL_CONTEXT_MAJOR_VERSION, 1, - EGL_CONTEXT_MINOR_VERSION, 5, - EGL_CONTEXT_OPENGL_PROFILE_MASK, - EGL_CONTEXT_OPENGL_COMPATIBILITY_PROFILE_BIT, - EGL_NONE}; - egl_context_ = - eglCreateContext(egl_display_, config, EGL_NO_CONTEXT, context_attribs); - if (egl_context_ == EGL_NO_CONTEXT) { - mju_error("eglCreateContext error: 0x%x", eglGetError()); - } - - // Make the EGL context current. - if (!eglMakeCurrent(egl_display_, nullptr, nullptr, egl_context_)) { - mju_error("eglMakeCurrent error: 0x%x", eglGetError()); - } -#else - mju_error("EGL not supported on this platform"); -#endif -} - } // namespace mujoco::platform diff --git a/src/experimental/platform/window.h b/src/experimental/platform/window.h index 61394375..2c53c85c 100644 --- a/src/experimental/platform/window.h +++ b/src/experimental/platform/window.h @@ -22,10 +22,6 @@ #include "experimental/platform/renderer_backend.h" -#if __has_include("third_party/GL/gl/include/EGL/egl.h") -#define MUJOCO_STUDIO_EGL_SUPPORTED 1 -#include "third_party/GL/gl/include/EGL/egl.h" -#endif #include #include @@ -107,10 +103,6 @@ class Window { SDL_Renderer* sdl_renderer_ = nullptr; bool should_exit_ = false; std::string drop_file_; - #ifdef MUJOCO_STUDIO_EGL_SUPPORTED - EGLDisplay egl_display_ = EGL_NO_DISPLAY; - EGLContext egl_context_ = EGL_NO_CONTEXT; - #endif }; } // namespace mujoco::platform