Remove `mjWARN_VGEOMFULL, handle visual geom buffer full warnings in mjvScene`.

PiperOrigin-RevId: 896483023
Change-Id: I6dde2c20d8e8e229cf95e1f002dd10525d3376e6
This commit is contained in:
Yuval Tassa
2026-04-08 07:15:59 -07:00
committed by Copybara-Service
parent 5a2cc6cee3
commit 0e04436d51
17 changed files with 47 additions and 53 deletions
+2
View File
@@ -44,6 +44,8 @@ General
- The ``vertcollide`` field in :ref:`mjsFlex` has been removed. It is no longer required since - The ``vertcollide`` field in :ref:`mjsFlex` has been removed. It is no longer required since
:doc:`MuJoCo Warp <mjwarp/index>` supports native flex collisions. :doc:`MuJoCo Warp <mjwarp/index>` supports native flex collisions.
- The :ref:`mjtWarning` enum value ``mjWARN_VGEOMFULL`` is removed. Exhaustion of visual geoms is now handled
internally by the :ref:`mjvScene`.
Bug fixes Bug fixes
^^^^^^^^^ ^^^^^^^^^
+3 -2
View File
@@ -63,7 +63,6 @@ typedef enum mjtWarning_ { // warning types
mjWARN_INERTIA = 0, // (near) singular inertia matrix mjWARN_INERTIA = 0, // (near) singular inertia matrix
mjWARN_CONTACTFULL, // too many contacts in contact list mjWARN_CONTACTFULL, // too many contacts in contact list
mjWARN_CNSTRFULL, // too many constraints mjWARN_CNSTRFULL, // too many constraints
mjWARN_VGEOMFULL, // too many visual geoms
mjWARN_BADQPOS, // bad number in qpos mjWARN_BADQPOS, // bad number in qpos
mjWARN_BADQVEL, // bad number in qvel mjWARN_BADQVEL, // bad number in qvel
mjWARN_BADQACC, // bad number in qacc mjWARN_BADQACC, // bad number in qacc
@@ -3096,7 +3095,9 @@ struct mjvScene_ { // abstract scene passed to OpenGL renderer
// framing // framing
int framewidth; // frame pixel width; 0: disable framing int framewidth; // frame pixel width; 0: disable framing
float framergb[3]; // frame color float framergb[3]; // frame color
int status; // status; 0: ok, 1: geoms exhausted
// geom buffer status
int status; // 0: ok, 1: geoms exhausted, warning issued
}; };
typedef struct mjvScene_ mjvScene; typedef struct mjvScene_ mjvScene;
struct mjvFigure_ { // abstract 2D figure passed to OpenGL renderer struct mjvFigure_ { // abstract 2D figure passed to OpenGL renderer
-1
View File
@@ -77,7 +77,6 @@ typedef enum mjtWarning_ { // warning types
mjWARN_INERTIA = 0, // (near) singular inertia matrix mjWARN_INERTIA = 0, // (near) singular inertia matrix
mjWARN_CONTACTFULL, // too many contacts in contact list mjWARN_CONTACTFULL, // too many contacts in contact list
mjWARN_CNSTRFULL, // too many constraints mjWARN_CNSTRFULL, // too many constraints
mjWARN_VGEOMFULL, // too many visual geoms
mjWARN_BADQPOS, // bad number in qpos mjWARN_BADQPOS, // bad number in qpos
mjWARN_BADQVEL, // bad number in qvel mjWARN_BADQVEL, // bad number in qvel
mjWARN_BADQACC, // bad number in qacc mjWARN_BADQACC, // bad number in qacc
+3 -1
View File
@@ -355,7 +355,9 @@ struct mjvScene_ { // abstract scene passed to OpenGL renderer
// framing // framing
int framewidth; // frame pixel width; 0: disable framing int framewidth; // frame pixel width; 0: disable framing
float framergb[3]; // frame color float framergb[3]; // frame color
int status; // status; 0: ok, 1: geoms exhausted
// geom buffer status
int status; // 0: ok, 1: geoms exhausted, warning issued
}; };
typedef struct mjvScene_ mjvScene; typedef struct mjvScene_ mjvScene;
+5 -1
View File
@@ -91,7 +91,11 @@ void SdfVisualizer::Visualize(const mjModel* m, const mjData* d,
for (int k = 0; k < 2; k++) { for (int k = 0; k < 2; k++) {
for (int j = 0; j < (k == 0 ? 2 : n-1); j++) { for (int j = 0; j < (k == 0 ? 2 : n-1); j++) {
if (scn->ngeom >= scn->maxgeom) { if (scn->ngeom >= scn->maxgeom) {
mj_warning((mjData*)d, mjWARN_VGEOMFULL, scn->maxgeom); if (!scn->status) {
mju_warning("Pre-allocated visual geom buffer is full. "
"Increase maxgeom above %d.", scn->maxgeom);
scn->status = 1;
}
return; return;
} }
mjvGeom* thisgeom = scn->geoms + scn->ngeom; mjvGeom* thisgeom = scn->geoms + scn->ngeom;
+5 -1
View File
@@ -427,7 +427,11 @@ void TouchGrid::Visualize(const mjModel* m, mjData* d, const mjvOption* opt,
continue; continue;
} }
if (scn->ngeom >= scn->maxgeom) { if (scn->ngeom >= scn->maxgeom) {
mj_warning(d, mjWARN_VGEOMFULL, scn->maxgeom); if (!scn->status) {
mju_warning("Pre-allocated visual geom buffer is full. "
"Increase maxgeom above %d.", scn->maxgeom);
scn->status = 1;
}
mj_freeStack(d); mj_freeStack(d);
return; return;
} else { } else {
+5 -6
View File
@@ -578,12 +578,11 @@ ENUMS: Mapping[str, EnumDecl] = dict([
('mjWARN_INERTIA', 0), ('mjWARN_INERTIA', 0),
('mjWARN_CONTACTFULL', 1), ('mjWARN_CONTACTFULL', 1),
('mjWARN_CNSTRFULL', 2), ('mjWARN_CNSTRFULL', 2),
('mjWARN_VGEOMFULL', 3), ('mjWARN_BADQPOS', 3),
('mjWARN_BADQPOS', 4), ('mjWARN_BADQVEL', 4),
('mjWARN_BADQVEL', 5), ('mjWARN_BADQACC', 5),
('mjWARN_BADQACC', 6), ('mjWARN_BADCTRL', 6),
('mjWARN_BADCTRL', 7), ('mjNWARNING', 7),
('mjNWARNING', 8),
]), ]),
)), )),
('mjtTimer', ('mjtTimer',
+2 -2
View File
@@ -5416,7 +5416,7 @@ STRUCTS: Mapping[str, StructDecl] = dict([
name='warning', name='warning',
type=ArrayType( type=ArrayType(
inner_type=ValueType(name='mjWarningStat'), inner_type=ValueType(name='mjWarningStat'),
extents=(8,), extents=(7,),
), ),
doc='warning statistics (mutable)', doc='warning statistics (mutable)',
), ),
@@ -10524,7 +10524,7 @@ STRUCTS: Mapping[str, StructDecl] = dict([
StructFieldDecl( StructFieldDecl(
name='status', name='status',
type=ValueType(name='int'), type=ValueType(name='int'),
doc='status; 0: ok, 1: geoms exhausted', doc='0: ok, 1: geoms exhausted, warning issued',
), ),
), ),
)), )),
+1 -3
View File
@@ -14,8 +14,6 @@
# ============================================================================== # ==============================================================================
"""Tests for structs.py.""" """Tests for structs.py."""
import re
from absl.testing import absltest from absl.testing import absltest
from . import ast_nodes from . import ast_nodes
@@ -36,7 +34,7 @@ class StructsTest(absltest.TestCase):
field_names.add(field.name) field_names.add(field.name)
if field.name == 'warning': if field.name == 'warning':
self.assertEqual(field.type, self.assertEqual(field.type,
type_parsing.parse_type('mjWarningStat[8]')) type_parsing.parse_type('mjWarningStat[7]'))
self.assertEqual(field.doc, 'warning statistics (mutable)') self.assertEqual(field.doc, 'warning statistics (mutable)')
elif field.name == 'qpos': elif field.name == 'qpos':
self.assertEqual(field.type, type_parsing.parse_type('mjtNum*')) self.assertEqual(field.type, type_parsing.parse_type('mjtNum*'))
+8 -8
View File
@@ -2142,11 +2142,7 @@ void Simulate::Sync(bool state_only) {
m_->stat = m_passive_->stat; m_->stat = m_passive_->stat;
} }
// synchronize number of mjWARN_VGEOMFULL warnings
if (d_passive_->warning[mjWARN_VGEOMFULL].number > warn_vgeomfull_prev_) {
d_->warning[mjWARN_VGEOMFULL].number +=
d_passive_->warning[mjWARN_VGEOMFULL].number - warn_vgeomfull_prev_;
}
} }
if (pending_.save_xml) { if (pending_.save_xml) {
@@ -2334,7 +2330,7 @@ void Simulate::Sync(bool state_only) {
mjopt_prev_ = m_passive_->opt; mjopt_prev_ = m_passive_->opt;
mjvis_prev_ = m_passive_->vis; mjvis_prev_ = m_passive_->vis;
mjstat_prev_ = m_passive_->stat; mjstat_prev_ = m_passive_->stat;
warn_vgeomfull_prev_ = d_passive_->warning[mjWARN_VGEOMFULL].number;
} }
// update settings // update settings
@@ -2550,7 +2546,7 @@ void Simulate::LoadOnRenderThread() {
mjopt_prev_ = m_->opt; mjopt_prev_ = m_->opt;
opt_prev_ = opt; opt_prev_ = opt;
cam_prev_ = cam; cam_prev_ = cam;
warn_vgeomfull_prev_ = d_->warning[mjWARN_VGEOMFULL].number;
// full copy on init // full copy on init
m_passive_ = mj_copyModel(nullptr, m_); m_passive_ = mj_copyModel(nullptr, m_);
@@ -3022,7 +3018,11 @@ void Simulate::RenderLoop() {
int nusergeom = user_scn_geoms_.size(); int nusergeom = user_scn_geoms_.size();
int ngeom = std::min(nusergeom, this->scn.maxgeom - this->scn.ngeom); int ngeom = std::min(nusergeom, this->scn.maxgeom - this->scn.ngeom);
if (ngeom < nusergeom) { if (ngeom < nusergeom) {
mj_warning(d_passive_, mjWARN_VGEOMFULL, this->scn.maxgeom); if (!this->scn.status) {
mju_warning("Pre-allocated visual geom buffer is full. "
"Increase maxgeom above %d.", this->scn.maxgeom);
this->scn.status = 1;
}
} }
std::memcpy(this->scn.geoms + this->scn.ngeom, user_scn_geoms_.data(), std::memcpy(this->scn.geoms + this->scn.ngeom, user_scn_geoms_.data(),
ngeom * sizeof(mjvGeom)); ngeom * sizeof(mjvGeom));
-2
View File
@@ -147,8 +147,6 @@ class Simulate {
mjvOption opt_prev_; mjvOption opt_prev_;
mjvCamera cam_prev_; mjvCamera cam_prev_;
int warn_vgeomfull_prev_;
// pending GUI-driven actions, to be applied at the next call to Sync // pending GUI-driven actions, to be applied at the next call to Sync
struct { struct {
std::optional<std::string> save_xml; std::optional<std::string> save_xml;
-4
View File
@@ -1595,10 +1595,6 @@ const char* mju_warningText(int warning, size_t info) {
"Increase arena memory allocation above %s bytes.", mju_writeNumBytes(info)); "Increase arena memory allocation above %s bytes.", mju_writeNumBytes(info));
break; break;
case mjWARN_VGEOMFULL:
mjSNPRINTF(str, "Pre-allocated visual geom buffer is full. Increase maxgeom above %zu.", info);
break;
case mjWARN_BADQPOS: case mjWARN_BADQPOS:
mjSNPRINTF(str, "Nan, Inf or huge value in QPOS at DOF %zu. The simulation is unstable.", info); mjSNPRINTF(str, "Nan, Inf or huge value in QPOS at DOF %zu. The simulation is unstable.", info);
break; break;
+5 -6
View File
@@ -169,7 +169,11 @@ static int bodycategory(const mjModel* m, int bodyid) {
mjvGeom* acquireGeom(mjvScene* scn, int objid, int category, int objtype) { mjvGeom* acquireGeom(mjvScene* scn, int objid, int category, int objtype) {
// check for overflow, SHOULD NOT OCCUR // check for overflow, SHOULD NOT OCCUR
if (scn->ngeom >= scn->maxgeom) { if (scn->ngeom >= scn->maxgeom) {
scn->status = 1; if (!scn->status) {
mju_warning("Pre-allocated visual geom buffer is full. "
"Increase maxgeom above %d.", scn->maxgeom);
scn->status = 1;
}
return NULL; return NULL;
} }
@@ -3379,7 +3383,6 @@ void mjv_updateScene(const mjModel* m, mjData* d, const mjvOption* opt,
const mjvPerturb* pert, mjvCamera* cam, int catmask, mjvScene* scn) { const mjvPerturb* pert, mjvCamera* cam, int catmask, mjvScene* scn) {
// clear geoms // clear geoms
scn->ngeom = 0; scn->ngeom = 0;
scn->status = 0;
// trigger plugin visualization hooks // trigger plugin visualization hooks
if (m->nplugin) { if (m->nplugin) {
@@ -3416,10 +3419,6 @@ void mjv_updateScene(const mjModel* m, mjData* d, const mjvOption* opt,
if (opt->flags[mjVIS_SKIN]) { if (opt->flags[mjVIS_SKIN]) {
mjv_updateActiveSkin(m, d, scn, opt); mjv_updateActiveSkin(m, d, scn, opt);
} }
if (scn->status) {
mj_warning(d, mjWARN_VGEOMFULL, scn->maxgeom);
}
} }
-1
View File
@@ -101,7 +101,6 @@ TEST_F(MjvSceneTest, UpdateSceneGeomsExhausted) {
mjv_updateScene(model, data, &opt_, &pert_, &cam_, mjCAT_ALL, &scn_); mjv_updateScene(model, data, &opt_, &pert_, &cam_, mjCAT_ALL, &scn_);
EXPECT_EQ(scn_.status, 1); EXPECT_EQ(scn_.status, 1);
EXPECT_EQ(scn_.ngeom, maxgeoms); EXPECT_EQ(scn_.ngeom, maxgeoms);
EXPECT_EQ(data->warning[mjWARN_VGEOMFULL].number, 1);
mj_deleteData(data); mj_deleteData(data);
FreeSceneObjects(); FreeSceneObjects();
+5 -7
View File
@@ -489,12 +489,11 @@ public enum mjtWarning : int{
mjWARN_INERTIA = 0, mjWARN_INERTIA = 0,
mjWARN_CONTACTFULL = 1, mjWARN_CONTACTFULL = 1,
mjWARN_CNSTRFULL = 2, mjWARN_CNSTRFULL = 2,
mjWARN_VGEOMFULL = 3, mjWARN_BADQPOS = 3,
mjWARN_BADQPOS = 4, mjWARN_BADQVEL = 4,
mjWARN_BADQVEL = 5, mjWARN_BADQACC = 5,
mjWARN_BADQACC = 6, mjWARN_BADCTRL = 6,
mjWARN_BADCTRL = 7, mjNWARNING = 7,
mjNWARNING = 8,
} }
public enum mjtTimer : int{ public enum mjtTimer : int{
mjTIMER_STEP = 0, mjTIMER_STEP = 0,
@@ -5699,7 +5698,6 @@ public unsafe struct mjData_ {
public mjWarningStat_ warning4; public mjWarningStat_ warning4;
public mjWarningStat_ warning5; public mjWarningStat_ warning5;
public mjWarningStat_ warning6; public mjWarningStat_ warning6;
public mjWarningStat_ warning7;
public mjTimerStat_ timer0; public mjTimerStat_ timer0;
public mjTimerStat_ timer1; public mjTimerStat_ timer1;
public mjTimerStat_ timer2; public mjTimerStat_ timer2;
+3 -7
View File
@@ -359,22 +359,18 @@ public class MjScene : MonoBehaviour {
} }
if (Data->warning3.number > 0) { if (Data->warning3.number > 0) {
Data->warning3.number = 0; Data->warning3.number = 0;
throw new PhysicsRuntimeException("VGEOMFULL: who constructed a mjvScene?!"); throw new PhysicsRuntimeException("BADQPOS: NaN/inf in qpos.");
} }
if (Data->warning4.number > 0) { if (Data->warning4.number > 0) {
Data->warning4.number = 0; Data->warning4.number = 0;
throw new PhysicsRuntimeException("BADQPOS: NaN/inf in qpos."); throw new PhysicsRuntimeException("BADQVEL: NaN/inf in qvel.");
} }
if (Data->warning5.number > 0) { if (Data->warning5.number > 0) {
Data->warning5.number = 0; Data->warning5.number = 0;
throw new PhysicsRuntimeException("BADQVEL: NaN/inf in qvel."); throw new PhysicsRuntimeException("BADQACC: NaN/inf in qacc.");
} }
if (Data->warning6.number > 0) { if (Data->warning6.number > 0) {
Data->warning6.number = 0; Data->warning6.number = 0;
throw new PhysicsRuntimeException("BADQACC: NaN/inf in qacc.");
}
if (Data->warning7.number > 0) {
Data->warning7.number = 0;
throw new PhysicsRuntimeException("BADCTRL: NaN/inf in ctrl."); throw new PhysicsRuntimeException("BADCTRL: NaN/inf in ctrl.");
} }
} }
-1
View File
@@ -11380,7 +11380,6 @@ EMSCRIPTEN_BINDINGS(mujoco_bindings) {
.value("mjWARN_INERTIA", mjWARN_INERTIA) .value("mjWARN_INERTIA", mjWARN_INERTIA)
.value("mjWARN_CONTACTFULL", mjWARN_CONTACTFULL) .value("mjWARN_CONTACTFULL", mjWARN_CONTACTFULL)
.value("mjWARN_CNSTRFULL", mjWARN_CNSTRFULL) .value("mjWARN_CNSTRFULL", mjWARN_CNSTRFULL)
.value("mjWARN_VGEOMFULL", mjWARN_VGEOMFULL)
.value("mjWARN_BADQPOS", mjWARN_BADQPOS) .value("mjWARN_BADQPOS", mjWARN_BADQPOS)
.value("mjWARN_BADQVEL", mjWARN_BADQVEL) .value("mjWARN_BADQVEL", mjWARN_BADQVEL)
.value("mjWARN_BADQACC", mjWARN_BADQACC) .value("mjWARN_BADQACC", mjWARN_BADQACC)