From c70b05e0371c0a9aff7d8a3ed6fce7b1aaa09262 Mon Sep 17 00:00:00 2001 From: Haroon Qureshi Date: Wed, 4 Mar 2026 07:25:51 -0800 Subject: [PATCH] Make Renderer more self-contained. The Renderer should configure the context and flip the image when in classic mode and not rely on the Window to do so. PiperOrigin-RevId: 878480986 Change-Id: I1af899107647469659662abd85527cdf66f507f4 --- src/experimental/platform/renderer.cc | 26 ++++++++++++++++++++++++-- src/experimental/platform/window.cc | 14 +------------- 2 files changed, 25 insertions(+), 15 deletions(-) diff --git a/src/experimental/platform/renderer.cc b/src/experimental/platform/renderer.cc index b00637cd..3688cf27 100644 --- a/src/experimental/platform/renderer.cc +++ b/src/experimental/platform/renderer.cc @@ -17,6 +17,7 @@ #include #include #include +#include #include @@ -31,11 +32,24 @@ namespace mujoco::platform { +static void FlipImage(unsigned char* pixels, int width, int height, int bpp) { + const int row_size = width * bpp; + for (int i = 0; i < height / 2; ++i) { + unsigned char* top_row = pixels + i * row_size; + unsigned char* bottom_row = pixels + (height - 1 - i) * row_size; + for (int j = 0; j < row_size; ++j) { + std::swap(top_row[j], bottom_row[j]); + } + } +} + Renderer::Renderer(void* native_window, GraphicsMode gfx) : native_window_(native_window), gfx_(gfx) { if (IsClassic(gfx_)) { if (native_window == nullptr) { - // graphics_api_context_ = CreateEglContext(); + #ifndef __EMSCRIPTEN__ + graphics_api_context_ = CreateEglContext(); + #endif // __EMSCRIPTEN__ } if (ImGui::GetCurrentContext()) { ImGui_ImplOpenGL3_Init(); @@ -165,6 +179,9 @@ void Renderer::Render(const mjModel* model, mjData* data, if (render_to_texture) { unsigned char* ptr = reinterpret_cast(pixels.data()); read_pixels_(ptr, viewport); + if (IsClassic(gfx_)) { + FlipImage(ptr, width, height, 3); + } } UpdateFps(); @@ -182,7 +199,12 @@ void Renderer::RenderToTexture(const mjModel* model, mjData* data, set_buffer_(mjFB_OFFSCREEN); render_(viewport, &scene_); - read_pixels_((unsigned char*)output, viewport); + + unsigned char* ptr = reinterpret_cast(output); + read_pixels_(ptr, viewport); + if (IsClassic(gfx_)) { + FlipImage(ptr, width, height, 3); + } } int Renderer::UploadImage(int texture_id, const std::byte* pixels, int width, diff --git a/src/experimental/platform/window.cc b/src/experimental/platform/window.cc index 0f849977..466db1a9 100644 --- a/src/experimental/platform/window.cc +++ b/src/experimental/platform/window.cc @@ -140,8 +140,7 @@ Window::Window(std::string_view title, int width, int height, Config config) // Filament (except WebGL) manages its own swap chain including when to swap. // In all other cases, we'll use SDL to manage the swap chain. if (config_.gfx_mode == GraphicsMode::FilamentWebGl || - config_.gfx_mode == GraphicsMode::ClassicOpenGl || - config_.gfx_mode == GraphicsMode::ClassicOpenGlHeadless) { + config_.gfx_mode == GraphicsMode::ClassicOpenGl) { SDL_GLContext gl_context = SDL_GL_CreateContext(sdl_window_); SDL_GL_MakeCurrent(sdl_window_, gl_context); } @@ -260,17 +259,6 @@ void Window::Present(std::span pixels) { } } - // Flip the image vertically because the classic renderer uses a bottom-left - // coordinate system while SDL assumes a top-left coordinate system. - if (config_.gfx_mode == GraphicsMode::ClassicOpenGlHeadless) { - dst = static_cast(surface->pixels); - for (int r = 0; r < height_ / 2; ++r) { - unsigned char* top_row = &dst[4 * width_ * r]; - unsigned char* bottom_row = &dst[4 * width_ * (height_ - 1 - r)]; - std::swap_ranges(top_row, top_row + 4 * width_, bottom_row); - } - } - SDL_RenderPresent(sdl_renderer_); } else if (config_.gfx_mode != GraphicsMode::FilamentVulkan && config_.gfx_mode != GraphicsMode::FilamentOpenGl) {