fix(mujoco): prevent viewer PiP flicker

This commit is contained in:
lgv 2026-09-04 11:01:04 +08:00
parent 2cfe354c07
commit e05a03e075
2 changed files with 35 additions and 2 deletions

View File

@ -109,6 +109,8 @@ namespace cmvr {
mjvScene pip_scene_; mjvScene pip_scene_;
bool pip_scene_inited_ = false; bool pip_scene_inited_ = false;
mjModel *pip_scene_model_ = nullptr; mjModel *pip_scene_model_ = nullptr;
mjData *pip_render_data_ = nullptr;
mjModel *pip_render_data_model_ = nullptr;
mutable std::mutex pip_rgb_mtx_; mutable std::mutex pip_rgb_mtx_;
std::vector<unsigned char> pip_rgb_; std::vector<unsigned char> pip_rgb_;
std::vector<float> pip_depth_; // 新增:z-buffer std::vector<float> pip_depth_; // 新增:z-buffer

View File

@ -76,6 +76,11 @@ namespace cmvr {
mjv_freeScene(&pip_scene_); mjv_freeScene(&pip_scene_);
pip_scene_inited_ = false; pip_scene_inited_ = false;
} }
if (pip_render_data_ != nullptr) {
mj_deleteData(pip_render_data_);
pip_render_data_ = nullptr;
pip_render_data_model_ = nullptr;
}
} }
mjModel* MuJocoViewer::model() const { mjModel* MuJocoViewer::model() const {
@ -207,7 +212,33 @@ namespace cmvr {
display_rect.left = left; display_rect.left = left;
display_rect.bottom = bottom; display_rect.bottom = bottom;
const std::unique_lock<std::recursive_mutex> lock(sim_->mtx); // Copy only the visualization state while synchronized with Simulate.
// Keep scene update, GPU rendering and pixel readback out of this lock
// so the world sync thread cannot hold the simulation mutex while
// waiting for the viewer.
{
std::unique_lock<std::recursive_mutex> lock(sim_->mtx, std::try_to_lock);
if (lock.owns_lock()) {
if (pip_render_data_model_ != render_model) {
if (pip_render_data_ != nullptr) {
mj_deleteData(pip_render_data_);
pip_render_data_ = nullptr;
}
pip_render_data_ = mj_makeData(render_model);
pip_render_data_model_ = render_model;
}
if (pip_render_data_ != nullptr) {
mjv_copyData(pip_render_data_, render_model, render_data);
}
}
}
// The simulation lock is intentionally non-blocking. If the sync
// thread owns it, use the last complete snapshot so every back buffer
// still gets the PiP overlay; skip only until the first snapshot exists.
if (pip_render_data_ == nullptr || pip_render_data_model_ != render_model) {
return;
}
if (!pip_scene_inited_ || pip_scene_model_ != render_model) { if (!pip_scene_inited_ || pip_scene_model_ != render_model) {
if (pip_scene_inited_) { if (pip_scene_inited_) {
@ -222,7 +253,7 @@ namespace cmvr {
pip_cam_.fixedcamid = pip_camera_id_; pip_cam_.fixedcamid = pip_camera_id_;
pip_cam_.trackbodyid = -1; pip_cam_.trackbodyid = -1;
mjv_updateScene(render_model, render_data, &opt_, &pert_, &pip_cam_, mjCAT_ALL, &pip_scene_); mjv_updateScene(render_model, pip_render_data_, &opt_, &pert_, &pip_cam_, mjCAT_ALL, &pip_scene_);
auto& context = sim_->platform_ui->mjr_context(); auto& context = sim_->platform_ui->mjr_context();
const int offscreen_width = context.offWidth > 0 ? context.offWidth : display_rect.width; const int offscreen_width = context.offWidth > 0 ? context.offWidth : display_rect.width;