Move all specialized rendering logic into Renderer class.

(We may consider splitting this class up into specialized subclasses at a later date.)

PiperOrigin-RevId: 851290789
Change-Id: Iee00b8e906eeb911de5799f784475ac6916ab55b
This commit is contained in:
Haroon Qureshi
2026-01-02 04:22:34 -08:00
committed by Copybara-Service
parent 14e90f3897
commit b9fa8ee6e9
6 changed files with 135 additions and 138 deletions
+19
View File
@@ -16,8 +16,25 @@ cmake_minimum_required(VERSION 3.16)
set(MUJOCO_PLATFORM_TARGET_NAME mujoco_platform)
# Determine the render configuration and dependencies based options.
if(MUJOCO_USE_FILAMENT AND MUJOCO_USE_FILAMENT_VULKAN)
set(MUJOCO_PLATFORM_RENDER_CONFIG "USE_FILAMENT_VULKAN")
set(MUJOCO_PLATFORM_RENDER_DEPS "mujoco::filament")
elseif(MUJOCO_USE_FILAMENT)
set(MUJOCO_PLATFORM_RENDER_CONFIG "USE_FILAMENT_OPENGL")
set(MUJOCO_PLATFORM_RENDER_DEPS "mujoco::filament")
else()
set(MUJOCO_PLATFORM_RENDER_CONFIG "USE_CLASSIC_OPENGL")
set(MUJOCO_PLATFORM_RENDER_DEPS "dear_imgui_OpenGL3")
endif()
add_library(${MUJOCO_PLATFORM_TARGET_NAME} STATIC)
target_compile_definitions(${MUJOCO_STUDIO_TARGET_NAME}
PUBLIC
-D${MUJOCO_PLATFORM_RENDER_CONFIG}
)
target_sources(${MUJOCO_PLATFORM_TARGET_NAME}
PUBLIC
gui.cc
@@ -61,9 +78,11 @@ include(third_party_deps/libwebp)
target_link_libraries(${MUJOCO_PLATFORM_TARGET_NAME}
dear_imgui
dear_imgui_SDL2
implot
webp
SDL2::SDL2-static
${MUJOCO_PLATFORM_RENDER_DEPS}
)
add_library(mujoco::platform ALIAS ${MUJOCO_PLATFORM_TARGET_NAME})
+90 -6
View File
@@ -14,14 +14,34 @@
#include "experimental/platform/renderer.h"
#include <chrono>
#include <cstddef>
#include <cstdint>
#include <cstdlib>
#include <cstring>
#include <vector>
#include <cstddef>
#include <mujoco/mujoco.h>
#include "experimental/platform/helpers.h"
#if defined(USE_FILAMENT_OPENGL) || defined(USE_FILAMENT_VULKAN)
#include "experimental/filament/render_context_filament.h"
#elif defined(USE_CLASSIC_OPENGL)
#include <imgui.h>
#include <backends/imgui_impl_opengl3.h>
#else
#error No rendering mode defined.
#endif
namespace mujoco::platform {
Renderer::Renderer(MakeContextFn make_context_fn)
: make_context_fn_(make_context_fn) {
Renderer::Renderer(void* native_window, const LoadAssetFn& load_asset_fn)
: load_asset_fn_(load_asset_fn), native_window_(native_window) {
#ifdef USE_CLASSIC_OPENGL
ImGui_ImplOpenGL3_Init();
#endif
}
Renderer::~Renderer() { Deinit(); }
@@ -30,7 +50,24 @@ void Renderer::Init(const mjModel* model) {
Deinit();
if (model) {
mjr_defaultContext(&render_context_);
make_context_fn_(model, &render_context_);
#if defined(USE_CLASSIC_OPENGL)
mjr_makeContext(model, &render_context_, mjFONTSCALE_150);
#else
mjrFilamentConfig render_config;
mjr_defaultFilamentConfig(&render_config);
render_config.native_window = native_window_;
render_config.load_asset = &Renderer::LoadAssetCallback;
render_config.load_asset_user_data = this;
render_config.enable_gui = true;
#if defined(USE_FILAMENT_OPENGL)
render_config.graphics_api = mjGFX_OPENGL;
#elif defined(USE_FILAMENT_VULKAN)
render_config.graphics_api = mjGFX_VULKAN;
#endif
mjr_makeFilamentContext(model, &render_context_, &render_config);
#endif
mjv_defaultScene(&scene_);
mjv_makeScene(model, &scene_, 2000);
initialized_ = true;
@@ -52,17 +89,43 @@ void Renderer::Render(const mjModel* model, mjData* data,
return;
}
mjv_updateScene(model, data, vis_option, perturb, camera, mjCAT_ALL,
&scene_);
if (last_update_time_ == data->time) {
mjv_updateCamera(model, data, camera, &scene_);
} else {
mjv_updateScene(model, data, vis_option, perturb, camera, mjCAT_ALL,
&scene_);
last_update_time_ = data->time;
}
const mjrRect viewport = {0, 0, width, height};
mjr_render(viewport, &scene_, &render_context_);
#ifdef USE_CLASSIC_OPENGL
ImGui_ImplOpenGL3_NewFrame();
ImGui::Render();
ImGui_ImplOpenGL3_RenderDrawData(ImGui::GetDrawData());
#endif
#ifdef USE_CLASSIC_OPENGL
TimePoint now = std::chrono::steady_clock::now();
TimePoint::duration delta_time = now - last_fps_update_;
const double interval = std::chrono::duration<double>(delta_time).count();
++frames_;
if (interval > 0.2) { // only update FPS stat at most 5 times per second
last_fps_update_ = now;
fps_ = frames_ / interval;
frames_ = 0;
}
#else
fps_ = mjr_getFrameRate(&render_context_);
#endif
}
void Renderer::RenderToTexture(const mjModel* model, mjData* data,
mjvCamera* camera, int width, int height,
std::byte* output) {
if (!initialized_) {
if (!initialized_ || last_update_time_ == -1) {
return;
}
@@ -75,4 +138,25 @@ void Renderer::RenderToTexture(const mjModel* model, mjData* data,
mjr_setBuffer(mjFB_WINDOW, &render_context_);
}
double Renderer::GetFps() { return fps_; }
int Renderer::LoadAssetCallback(const char* path, void* user_data,
unsigned char** out, std::uint64_t* out_size) {
Renderer* renderer = static_cast<Renderer*>(user_data);
std::vector<std::byte> bytes = (renderer->load_asset_fn_)(path);
if (bytes.empty()) {
*out_size = 0;
return 0; // Empty file
}
*out_size = bytes.size();
*out = reinterpret_cast<unsigned char*>(malloc(*out_size));
if (*out == nullptr) {
mju_error("Failed to allocate memory for file %s", path);
return -1;
}
std::memcpy(*out, bytes.data(), *out_size);
return 0;
}
} // namespace mujoco::platform
+20 -10
View File
@@ -15,10 +15,13 @@
#ifndef MUJOCO_SRC_EXPERIMENTAL_PLATFORM_RENDERER_H_
#define MUJOCO_SRC_EXPERIMENTAL_PLATFORM_RENDERER_H_
#include <chrono>
#include <cstddef>
#include <functional>
#include <cstdint>
#include <ratio>
#include <mujoco/mujoco.h>
#include "experimental/platform/helpers.h"
namespace mujoco::platform {
@@ -26,12 +29,12 @@ namespace mujoco::platform {
// using the filament rendering backend.
class Renderer {
public:
// Function that creates a mjrContext for the given model. We use a function
// to allow different mjrContext implementations to be created without
// requiring a direct dependency on them.
using MakeContextFn = std::function<void(const mjModel* m, mjrContext* con)>;
using Clock = std::chrono::steady_clock;
using TimePoint = std::chrono::time_point<Clock>;
using Seconds = std::chrono::duration<double>;
using Milliseconds = std::chrono::duration<double, std::milli>;
explicit Renderer(MakeContextFn make_context_fn);
Renderer(void* native_window, const LoadAssetFn& load_asset_fn);
~Renderer();
Renderer(const Renderer&) = delete;
@@ -54,18 +57,25 @@ class Renderer {
// Rendering flags.
mjtByte* GetRenderFlags() { return scene_.flags; }
// Returns the render context.
mjrContext& GetContext() { return render_context_; }
const mjrContext& GetContext() const { return render_context_; }
// Returns the current frame rate.
double GetFps();
private:
// Resets the renderer; no rendering will occur until Init() is called again.
void Deinit();
MakeContextFn make_context_fn_;
static int LoadAssetCallback(const char* path, void* user_data,
unsigned char** out, std::uint64_t* out_size);
LoadAssetFn load_asset_fn_;
void* native_window_ = nullptr;
mjrContext render_context_;
mjvScene scene_;
bool initialized_ = false;
mjtNum last_update_time_ = -1;
int frames_ = 0;
TimePoint last_fps_update_;
double fps_ = 0;
};
} // namespace mujoco::platform