diff --git a/cmvr-es/simulate/mujoco/mujoco_viewer/include/mujoco_viewer.h b/cmvr-es/simulate/mujoco/mujoco_viewer/include/mujoco_viewer.h index 9af82f7a..74a18731 100644 --- a/cmvr-es/simulate/mujoco/mujoco_viewer/include/mujoco_viewer.h +++ b/cmvr-es/simulate/mujoco/mujoco_viewer/include/mujoco_viewer.h @@ -109,6 +109,8 @@ namespace cmvr { mjvScene pip_scene_; bool pip_scene_inited_ = false; mjModel *pip_scene_model_ = nullptr; + mjData *pip_render_data_ = nullptr; + mjModel *pip_render_data_model_ = nullptr; mutable std::mutex pip_rgb_mtx_; std::vector pip_rgb_; std::vector pip_depth_; // 新增:z-buffer diff --git a/cmvr-es/simulate/mujoco/mujoco_viewer/src/mujoco_viewer.cpp b/cmvr-es/simulate/mujoco/mujoco_viewer/src/mujoco_viewer.cpp index 300456dd..a5d84cd5 100644 --- a/cmvr-es/simulate/mujoco/mujoco_viewer/src/mujoco_viewer.cpp +++ b/cmvr-es/simulate/mujoco/mujoco_viewer/src/mujoco_viewer.cpp @@ -76,6 +76,11 @@ namespace cmvr { mjv_freeScene(&pip_scene_); 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 { @@ -207,7 +212,33 @@ namespace cmvr { display_rect.left = left; display_rect.bottom = bottom; - const std::unique_lock 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 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_) { @@ -222,7 +253,7 @@ namespace cmvr { pip_cam_.fixedcamid = pip_camera_id_; 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(); const int offscreen_width = context.offWidth > 0 ? context.offWidth : display_rect.width;