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
This commit is contained in:
Haroon Qureshi
2026-02-03 08:11:46 -08:00
committed by Copybara-Service
parent 7ec8b6e341
commit ef8e6802ef
7 changed files with 185 additions and 109 deletions
+2
View File
@@ -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
+144
View File
@@ -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 <memory>
#include "third_party/GL/gl/include/EGL/egl.h"
#include "third_party/GL/gl/include/EGL/eglext.h"
#include <mujoco/mujoco.h>
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<void> CreateEglContext() {
auto egl_context = std::make_shared<EglContext>();
return std::static_pointer_cast<void>(egl_context);
}
} // namespace mujoco::platform
+28
View File
@@ -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 <memory>
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<void> CreateEglContext();
} // namespace mujoco::platform
#endif // MUJOCO_SRC_EXPERIMENTAL_PLATFORM_EGL_UTILS_H_
+8 -2
View File
@@ -23,6 +23,7 @@
#if defined(MUJOCO_RENDERER_CLASSIC_OPENGL)
#include <imgui.h>
#include <backends/imgui_impl_opengl3.h>
#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)
+3
View File
@@ -17,12 +17,14 @@
#include <chrono>
#include <cstddef>
#include <memory>
#include <ratio>
#include <span>
#include <mujoco/mujoco.h>
#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<void> graphics_api_context_ = nullptr;
mjrContext render_context_;
mjvScene scene_;
bool initialized_ = false;
-99
View File
@@ -20,10 +20,6 @@
#include <string>
#include <string_view>
#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 <SDL.h>
#include <SDL_error.h>
#include <SDL_events.h>
@@ -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<const std::byte> 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
-8
View File
@@ -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 <SDL_video.h>
#include <SDL_render.h>
@@ -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