Add compiler timing diagnostics to mjsCompiler, printed by compile.cc
For example, `compile mujoco_menagerie/robotis_op3/scene.xml` now outputs
```
Compile 1 (cold cache):
total: 317.2 ms
assets: 284.8 ms (wall clock)
load: 616.1 ms
hull: 26.4 ms
poly: 137.8 ms
inert: 177.8 ms
bvh: 568.2 ms
octr: 1.6 ms
tex: 25.3 ms
other: 32.4 ms
Compile 2 (warm cache):
total: 79.9 ms
assets: 54.5 ms (wall clock)
load: 888.5 ms
hull: 0.0 ms
poly: 0.0 ms
inert: 0.0 ms
bvh: 0.0 ms
octr: 0.0 ms
tex: 21.4 ms
other: 25.3 ms
```
PiperOrigin-RevId: 917850214
Change-Id: Iaec86230bec0faf2e47820e20cbff61de5b2621e
This commit is contained in:
committed by
Copybara-Service
parent
f712eed4ce
commit
bdf00966f9
@@ -828,6 +828,18 @@ Type of built-in procedural mesh.
|
|||||||
.. mujoco-include:: mjtMeshBuiltin
|
.. mujoco-include:: mjtMeshBuiltin
|
||||||
|
|
||||||
|
|
||||||
|
.. _mjtCTimer:
|
||||||
|
|
||||||
|
mjtCTimer
|
||||||
|
~~~~~~~~~
|
||||||
|
|
||||||
|
Compiler timing categories, used in :ref:`mjs_getTimer`. Top-level timers (``TOTAL``, ``ASSETS``) measure wall-clock
|
||||||
|
time. Asset sub-timers measure CPU time summed across all assets; with multi-threaded compilation their sum can exceed
|
||||||
|
the ``ASSETS`` wall-clock time.
|
||||||
|
|
||||||
|
.. mujoco-include:: mjtCTimer
|
||||||
|
|
||||||
|
|
||||||
.. _tyPluginEnums:
|
.. _tyPluginEnums:
|
||||||
|
|
||||||
Plugins
|
Plugins
|
||||||
|
|||||||
@@ -2079,6 +2079,15 @@ Write [datetime, type: message] to MUJOCO_LOG.TXT.
|
|||||||
|
|
||||||
Get compiler error message from spec.
|
Get compiler error message from spec.
|
||||||
|
|
||||||
|
.. _mjs_getTimer:
|
||||||
|
|
||||||
|
`mjs_getTimer <#mjs_getTimer>`__
|
||||||
|
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||||
|
|
||||||
|
.. mujoco-include:: mjs_getTimer
|
||||||
|
|
||||||
|
Get compiler timing diagnostics from spec, returns pointer to array of size mjNCTIMER.
|
||||||
|
|
||||||
.. _mjs_isWarning:
|
.. _mjs_isWarning:
|
||||||
|
|
||||||
`mjs_isWarning <#mjs_isWarning>`__
|
`mjs_isWarning <#mjs_isWarning>`__
|
||||||
|
|||||||
@@ -17,6 +17,10 @@ General
|
|||||||
release, now uses a fixed seed. The previous implementation seeded with ``mjData.time``, which introduced subtle yet
|
release, now uses a fixed seed. The previous implementation seeded with ``mjData.time``, which introduced subtle yet
|
||||||
undesirable time dependence.
|
undesirable time dependence.
|
||||||
- Flexes are now allowed to sleep, with the exception of completely passive (constraint-free) flexes.
|
- Flexes are now allowed to sleep, with the exception of completely passive (constraint-free) flexes.
|
||||||
|
- Added compiler timing diagnostics via the new :ref:`mjtCTimer` enum and the :ref:`mjs_getTimer` C API. After
|
||||||
|
:ref:`mj_compile`, per-category timings (total, assets, mesh loading, convex hull, normals, inertia, BVH, octree,
|
||||||
|
textures) are available via ``mjs_getTimer(spec)``. The :ref:`compile<saCompile>` sample prints a detailed timing
|
||||||
|
breakdown when run without an output file.
|
||||||
|
|
||||||
.. admonition:: Breaking API changes
|
.. admonition:: Breaking API changes
|
||||||
:class: attention
|
:class: attention
|
||||||
|
|||||||
@@ -1965,6 +1965,22 @@ typedef enum mjtOrientation_ { // type of orientation specifier
|
|||||||
mjORIENTATION_ZAXIS, // z axis (minimal rotation)
|
mjORIENTATION_ZAXIS, // z axis (minimal rotation)
|
||||||
mjORIENTATION_EULER, // Euler angles
|
mjORIENTATION_EULER, // Euler angles
|
||||||
} mjtOrientation;
|
} mjtOrientation;
|
||||||
|
typedef enum mjtCTimer_ { // compiler timing categories
|
||||||
|
// top-level timers (wall-clock)
|
||||||
|
mjCTIMER_TOTAL = 0, // total compile time
|
||||||
|
mjCTIMER_ASSETS, // asset compilation
|
||||||
|
|
||||||
|
// asset sub-timers (CPU time, summed across all assets)
|
||||||
|
mjCTIMER_TEXTURE, // textures
|
||||||
|
mjCTIMER_MESH_LOAD, // mesh: file loading
|
||||||
|
mjCTIMER_MESH_HULL, // mesh: convex hull
|
||||||
|
mjCTIMER_MESH_POLYGON, // mesh: normals and polygons
|
||||||
|
mjCTIMER_MESH_INERTIA, // mesh: volume, CoM, inertia
|
||||||
|
mjCTIMER_MESH_BVH, // mesh: bounding volume hierarchy
|
||||||
|
mjCTIMER_MESH_OCTREE, // mesh: octree and SDF
|
||||||
|
|
||||||
|
mjNCTIMER // number of compiler timers
|
||||||
|
} mjtCTimer;
|
||||||
typedef struct mjsElement_ { // element type, do not modify
|
typedef struct mjsElement_ { // element type, do not modify
|
||||||
mjtObj elemtype; // element type
|
mjtObj elemtype; // element type
|
||||||
uint64_t signature; // compilation signature
|
uint64_t signature; // compilation signature
|
||||||
@@ -3471,6 +3487,7 @@ void mju_free(void* ptr);
|
|||||||
void mj_warning(mjData* d, int warning, int info);
|
void mj_warning(mjData* d, int warning, int info);
|
||||||
void mju_writeLog(const char* type, const char* msg);
|
void mju_writeLog(const char* type, const char* msg);
|
||||||
const char* mjs_getError(mjSpec* s);
|
const char* mjs_getError(mjSpec* s);
|
||||||
|
const double* mjs_getTimer(mjSpec* s);
|
||||||
int mjs_isWarning(mjSpec* s);
|
int mjs_isWarning(mjSpec* s);
|
||||||
void mju_zero3(mjtNum res[3]);
|
void mju_zero3(mjtNum res[3]);
|
||||||
void mju_copy3(mjtNum res[3], const mjtNum data[3]);
|
void mju_copy3(mjtNum res[3], const mjtNum data[3]);
|
||||||
|
|||||||
@@ -120,16 +120,19 @@ Windows power plan so that the minimum processor state is 100%.
|
|||||||
`compile <https://github.com/google-deepmind/mujoco/blob/main/sample/compile.cc>`_
|
`compile <https://github.com/google-deepmind/mujoco/blob/main/sample/compile.cc>`_
|
||||||
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||||
|
|
||||||
This code sample evokes the built-in parser and compiler. It implements all possible model conversions from (MJCF, URDF,
|
This code sample invokes the built-in parser and compiler. It implements all possible model conversions from (MJCF,
|
||||||
MJB) format to (MJCF, MJB, TXT) format. Models saved as MJCF use a canonical subset of our format as described in the
|
URDF, MJB) format to (MJCF, MJB, TXT) format. Models saved as MJCF use a canonical subset of our format as described in
|
||||||
:doc:`../modeling` chapter, and therefore MJCF-to-MJCF conversion will generally result in a different file.
|
the :doc:`../modeling` chapter, and therefore MJCF-to-MJCF conversion will generally result in a different file.
|
||||||
The TXT format is a human-readable road-map to the model. It cannot be loaded by MuJoCo, but can be a very useful aid
|
The TXT format is a human-readable road-map to the model. It cannot be loaded by MuJoCo, but can be a very useful aid
|
||||||
during model development. It is in one-to-one correspondence with the compiled mjModel. Note also that one can use the
|
during model development. It is in one-to-one correspondence with the compiled mjModel. Note also that one can use the
|
||||||
function :ref:`mj_printData` to create a text file which is in one-to-one correspondence
|
function :ref:`mj_printData` to create a text file which is in one-to-one correspondence
|
||||||
with mjData, although this is not done by the code sample.
|
with mjData, although this is not done by the code sample.
|
||||||
|
|
||||||
If the input file is MJCF and the output file is empty, compilation is performed and timed twice to measure the impact
|
If the input file is MJCF or URDF and the output file is empty, compilation is performed twice to measure the impact
|
||||||
of the compiler's :ref:`asset cache<Assetcache>`.
|
of the compiler's :ref:`asset cache<Assetcache>`. A detailed timing breakdown is printed for each compilation, showing
|
||||||
|
total time, asset processing time (wall clock), and per-category CPU times for meshes and textures. These timings are
|
||||||
|
read from the :ref:`mjtCTimer` fields via :ref:`mjs_getTimer`, which can be read programmatically
|
||||||
|
after any call to :ref:`mj_compile`.
|
||||||
|
|
||||||
.. _saBasic:
|
.. _saBasic:
|
||||||
|
|
||||||
|
|||||||
@@ -129,6 +129,24 @@ typedef enum mjtOrientation_ { // type of orientation specifier
|
|||||||
} mjtOrientation;
|
} mjtOrientation;
|
||||||
|
|
||||||
|
|
||||||
|
typedef enum mjtCTimer_ { // compiler timing categories
|
||||||
|
// top-level timers (wall-clock)
|
||||||
|
mjCTIMER_TOTAL = 0, // total compile time
|
||||||
|
mjCTIMER_ASSETS, // asset compilation
|
||||||
|
|
||||||
|
// asset sub-timers (CPU time, summed across all assets)
|
||||||
|
mjCTIMER_TEXTURE, // textures
|
||||||
|
mjCTIMER_MESH_LOAD, // mesh: file loading
|
||||||
|
mjCTIMER_MESH_HULL, // mesh: convex hull
|
||||||
|
mjCTIMER_MESH_POLYGON, // mesh: normals and polygons
|
||||||
|
mjCTIMER_MESH_INERTIA, // mesh: volume, CoM, inertia
|
||||||
|
mjCTIMER_MESH_BVH, // mesh: bounding volume hierarchy
|
||||||
|
mjCTIMER_MESH_OCTREE, // mesh: octree and SDF
|
||||||
|
|
||||||
|
mjNCTIMER // number of compiler timers
|
||||||
|
} mjtCTimer;
|
||||||
|
|
||||||
|
|
||||||
//-------------------------------- attribute structs (mjs) -----------------------------------------
|
//-------------------------------- attribute structs (mjs) -----------------------------------------
|
||||||
|
|
||||||
typedef struct mjsElement_ { // element type, do not modify
|
typedef struct mjsElement_ { // element type, do not modify
|
||||||
|
|||||||
@@ -1005,6 +1005,9 @@ MJAPI void mju_writeLog(const char* type, const char* msg);
|
|||||||
// Get compiler error message from spec.
|
// Get compiler error message from spec.
|
||||||
MJAPI const char* mjs_getError(mjSpec* s);
|
MJAPI const char* mjs_getError(mjSpec* s);
|
||||||
|
|
||||||
|
// Get compiler timing diagnostics from spec, returns pointer to array of size mjNCTIMER.
|
||||||
|
MJAPI const double* mjs_getTimer(mjSpec* s);
|
||||||
|
|
||||||
// Return 1 if compiler error is a warning.
|
// Return 1 if compiler error is a warning.
|
||||||
MJAPI int mjs_isWarning(mjSpec* s);
|
MJAPI int mjs_isWarning(mjSpec* s);
|
||||||
|
|
||||||
|
|||||||
@@ -718,6 +718,23 @@ ENUMS: Mapping[str, EnumDecl] = dict([
|
|||||||
('mjORIENTATION_EULER', 4),
|
('mjORIENTATION_EULER', 4),
|
||||||
]),
|
]),
|
||||||
)),
|
)),
|
||||||
|
('mjtCTimer',
|
||||||
|
EnumDecl(
|
||||||
|
name='mjtCTimer',
|
||||||
|
declname='enum mjtCTimer_',
|
||||||
|
values=dict([
|
||||||
|
('mjCTIMER_TOTAL', 0),
|
||||||
|
('mjCTIMER_ASSETS', 1),
|
||||||
|
('mjCTIMER_TEXTURE', 2),
|
||||||
|
('mjCTIMER_MESH_LOAD', 3),
|
||||||
|
('mjCTIMER_MESH_HULL', 4),
|
||||||
|
('mjCTIMER_MESH_POLYGON', 5),
|
||||||
|
('mjCTIMER_MESH_INERTIA', 6),
|
||||||
|
('mjCTIMER_MESH_BVH', 7),
|
||||||
|
('mjCTIMER_MESH_OCTREE', 8),
|
||||||
|
('mjNCTIMER', 9),
|
||||||
|
]),
|
||||||
|
)),
|
||||||
('mjtCatBit',
|
('mjtCatBit',
|
||||||
EnumDecl(
|
EnumDecl(
|
||||||
name='mjtCatBit',
|
name='mjtCatBit',
|
||||||
|
|||||||
@@ -6471,6 +6471,22 @@ FUNCTIONS: Mapping[str, FunctionDecl] = dict([
|
|||||||
),
|
),
|
||||||
doc='Get compiler error message from spec.',
|
doc='Get compiler error message from spec.',
|
||||||
)),
|
)),
|
||||||
|
('mjs_getTimer',
|
||||||
|
FunctionDecl(
|
||||||
|
name='mjs_getTimer',
|
||||||
|
return_type=PointerType(
|
||||||
|
inner_type=ValueType(name='double', is_const=True),
|
||||||
|
),
|
||||||
|
parameters=(
|
||||||
|
FunctionParameterDecl(
|
||||||
|
name='s',
|
||||||
|
type=PointerType(
|
||||||
|
inner_type=ValueType(name='mjSpec'),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
doc='Get compiler timing diagnostics from spec, returns pointer to array of size mjNCTIMER.', # pylint: disable=line-too-long
|
||||||
|
)),
|
||||||
('mjs_isWarning',
|
('mjs_isWarning',
|
||||||
FunctionDecl(
|
FunctionDecl(
|
||||||
name='mjs_isWarning',
|
name='mjs_isWarning',
|
||||||
|
|||||||
@@ -55,6 +55,7 @@ using MjDouble3 = Eigen::Map<Eigen::Vector3d>;
|
|||||||
using MjDouble4 = Eigen::Map<Eigen::Vector4d>;
|
using MjDouble4 = Eigen::Map<Eigen::Vector4d>;
|
||||||
using MjDouble5 = Eigen::Map<Eigen::Matrix<double, 5, 1>>;
|
using MjDouble5 = Eigen::Map<Eigen::Matrix<double, 5, 1>>;
|
||||||
using MjDouble6 = Eigen::Map<Eigen::Matrix<double, 6, 1>>;
|
using MjDouble6 = Eigen::Map<Eigen::Matrix<double, 6, 1>>;
|
||||||
|
using MjDouble9 = Eigen::Map<Eigen::Matrix<double, 9, 1>>;
|
||||||
using MjDouble10 = Eigen::Map<Eigen::Matrix<double, 10, 1>>;
|
using MjDouble10 = Eigen::Map<Eigen::Matrix<double, 10, 1>>;
|
||||||
using MjDouble11 = Eigen::Map<Eigen::Matrix<double, 11, 1>>;
|
using MjDouble11 = Eigen::Map<Eigen::Matrix<double, 11, 1>>;
|
||||||
using MjDoubleVec = Eigen::Map<Eigen::VectorXd>;
|
using MjDoubleVec = Eigen::Map<Eigen::VectorXd>;
|
||||||
@@ -69,6 +70,7 @@ using MjDoubleRef3 = Eigen::Ref<const Eigen::Vector3d>;
|
|||||||
using MjDoubleRef4 = Eigen::Ref<const Eigen::Vector4d>;
|
using MjDoubleRef4 = Eigen::Ref<const Eigen::Vector4d>;
|
||||||
using MjDoubleRef5 = Eigen::Ref<const Eigen::Matrix<double, 5, 1>>;
|
using MjDoubleRef5 = Eigen::Ref<const Eigen::Matrix<double, 5, 1>>;
|
||||||
using MjDoubleRef6 = Eigen::Ref<const Eigen::Matrix<double, 6, 1>>;
|
using MjDoubleRef6 = Eigen::Ref<const Eigen::Matrix<double, 6, 1>>;
|
||||||
|
using MjDoubleRef9 = Eigen::Ref<const Eigen::Matrix<double, 9, 1>>;
|
||||||
using MjDoubleRef10 = Eigen::Ref<const Eigen::Matrix<double, 10, 1>>;
|
using MjDoubleRef10 = Eigen::Ref<const Eigen::Matrix<double, 10, 1>>;
|
||||||
using MjDoubleRef11 = Eigen::Ref<const Eigen::Matrix<double, 11, 1>>;
|
using MjDoubleRef11 = Eigen::Ref<const Eigen::Matrix<double, 11, 1>>;
|
||||||
using MjDoubleRefVec = Eigen::Ref<const Eigen::VectorXd>;
|
using MjDoubleRefVec = Eigen::Ref<const Eigen::VectorXd>;
|
||||||
@@ -461,6 +463,12 @@ PYBIND11_MODULE(_specs, m) {
|
|||||||
mjSpec.def_property_readonly("_address", [](const MjSpec& self) {
|
mjSpec.def_property_readonly("_address", [](const MjSpec& self) {
|
||||||
return reinterpret_cast<uintptr_t>(self.ptr);
|
return reinterpret_cast<uintptr_t>(self.ptr);
|
||||||
});
|
});
|
||||||
|
mjSpec.def_property_readonly(
|
||||||
|
"timer",
|
||||||
|
[](MjSpec& self) -> MjDouble9 {
|
||||||
|
return MjDouble9(const_cast<double*>(mjs_getTimer(self.ptr)));
|
||||||
|
},
|
||||||
|
py::return_value_policy::reference_internal);
|
||||||
mjSpec.def_property(
|
mjSpec.def_property(
|
||||||
"copy_during_attach",
|
"copy_during_attach",
|
||||||
[](MjSpec& self) {
|
[](MjSpec& self) {
|
||||||
|
|||||||
@@ -47,6 +47,24 @@ class SpecsTest(absltest.TestCase):
|
|||||||
self.assertIsInstance(spec.worldbody, mujoco.MjsBody)
|
self.assertIsInstance(spec.worldbody, mujoco.MjsBody)
|
||||||
self.assertIsInstance(spec.worldbody, typing.get_args(mujoco.MjStruct))
|
self.assertIsInstance(spec.worldbody, typing.get_args(mujoco.MjStruct))
|
||||||
|
|
||||||
|
def test_timer(self):
|
||||||
|
xml = """
|
||||||
|
<mujoco>
|
||||||
|
<asset>
|
||||||
|
<texture name="grid" type="2d" builtin="checker" width="300" height="300" rgb1=".1 .2 .3" rgb2=".2 .3 .4"/>
|
||||||
|
<material name="grid" texture="grid"/>
|
||||||
|
</asset>
|
||||||
|
<worldbody>
|
||||||
|
<geom type="plane" size="1 1 1" material="grid"/>
|
||||||
|
</worldbody>
|
||||||
|
</mujoco>
|
||||||
|
"""
|
||||||
|
spec = mujoco.MjSpec.from_string(xml)
|
||||||
|
model = spec.compile()
|
||||||
|
self.assertGreater(spec.timer[mujoco.mjtCTimer.mjCTIMER_TOTAL], 0)
|
||||||
|
self.assertGreater(spec.timer[mujoco.mjtCTimer.mjCTIMER_ASSETS], 0)
|
||||||
|
self.assertGreater(spec.timer[mujoco.mjtCTimer.mjCTIMER_TEXTURE], 0)
|
||||||
|
|
||||||
def test_basic(self):
|
def test_basic(self):
|
||||||
# Create a spec.
|
# Create a spec.
|
||||||
spec = mujoco.MjSpec()
|
spec = mujoco.MjSpec()
|
||||||
|
|||||||
+45
-28
@@ -25,11 +25,11 @@
|
|||||||
|
|
||||||
// help
|
// help
|
||||||
static constexpr char helpstring[] =
|
static constexpr char helpstring[] =
|
||||||
"\n Usage: compile infile outfile\n"
|
"\n Usage: compile infile [outfile]\n"
|
||||||
" infile can be in mjcf, urdf, mjb format\n"
|
" infile can be in mjcf, urdf, mjb format\n"
|
||||||
" outfile can be in mjcf, mjb, txt format, or empty\n\n"
|
" outfile can be in mjcf, mjb, txt format\n\n"
|
||||||
" if infile is mjcf and outfile is empty, compilation will be "
|
" if infile is mjcf or urdf and outfile is omitted, a detailed\n"
|
||||||
"timed twice to measure the impact of caching\n\n"
|
" timing breakdown is printed for two compilations (cold and warm cache)\n\n"
|
||||||
" Example: compile model.xml [model.mjb]\n";
|
" Example: compile model.xml [model.mjb]\n";
|
||||||
|
|
||||||
|
|
||||||
@@ -137,17 +137,45 @@ int main(int argc, char** argv) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// print compiler timing diagnostics
|
||||||
|
auto print_timers = [](const mjSpec* s, const char* label) {
|
||||||
|
const double* timer = mjs_getTimer(const_cast<mjSpec*>(s));
|
||||||
|
std::printf("\n%s:\n", label);
|
||||||
|
std::printf(" total: %8.1f ms\n", 1e3 * timer[mjCTIMER_TOTAL]);
|
||||||
|
std::printf(" assets: %8.1f ms (wall clock)\n", 1e3 * timer[mjCTIMER_ASSETS]);
|
||||||
|
std::printf(" load: %8.1f ms\n", 1e3 * timer[mjCTIMER_MESH_LOAD]);
|
||||||
|
std::printf(" hull: %8.1f ms\n", 1e3 * timer[mjCTIMER_MESH_HULL]);
|
||||||
|
std::printf(" poly: %8.1f ms\n", 1e3 * timer[mjCTIMER_MESH_POLYGON]);
|
||||||
|
std::printf(" inert: %8.1f ms\n", 1e3 * timer[mjCTIMER_MESH_INERTIA]);
|
||||||
|
std::printf(" bvh: %8.1f ms\n", 1e3 * timer[mjCTIMER_MESH_BVH]);
|
||||||
|
std::printf(" octr: %8.1f ms\n", 1e3 * timer[mjCTIMER_MESH_OCTREE]);
|
||||||
|
std::printf(" tex: %8.1f ms\n", 1e3 * timer[mjCTIMER_TEXTURE]);
|
||||||
|
std::printf(" other: %8.1f ms\n",
|
||||||
|
1e3 * (timer[mjCTIMER_TOTAL] - timer[mjCTIMER_ASSETS]));
|
||||||
|
};
|
||||||
|
|
||||||
// load model
|
// load model
|
||||||
double first=0, second=0;
|
mjSpec* s = nullptr;
|
||||||
if (type1==typeXML) {
|
if (type1==typeXML) {
|
||||||
double starttime = gettm();
|
s = mj_parseXML(argv[1], 0, error, 1000);
|
||||||
m = mj_loadXML(argv[1], 0, error, 1000);
|
if (!s) {
|
||||||
first = gettm() - starttime;
|
return finish(error, EXIT_FAILURE);
|
||||||
if (m && type2 == typeNONE) {
|
}
|
||||||
|
|
||||||
|
m = mj_compile(s, 0);
|
||||||
|
if (!m) {
|
||||||
|
mj_deleteSpec(s);
|
||||||
|
return finish("Could not compile model", EXIT_FAILURE);
|
||||||
|
}
|
||||||
|
|
||||||
|
print_timers(s, "Compile 1 (cold cache)");
|
||||||
|
|
||||||
|
if (type2 == typeNONE) {
|
||||||
mj_deleteModel(m);
|
mj_deleteModel(m);
|
||||||
starttime = gettm();
|
m = mj_compile(s, 0);
|
||||||
m = mj_loadXML(argv[1], 0, error, 1000);
|
if (m) {
|
||||||
second = gettm() - starttime;
|
print_timers(s, "Compile 2 (warm cache)");
|
||||||
|
}
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
m = mj_loadModel(argv[1], 0);
|
m = mj_loadModel(argv[1], 0);
|
||||||
@@ -155,16 +183,14 @@ int main(int argc, char** argv) {
|
|||||||
|
|
||||||
// check error
|
// check error
|
||||||
if (!m) {
|
if (!m) {
|
||||||
if (type1 == typeXML) {
|
if (s) mj_deleteSpec(s);
|
||||||
return finish(error, EXIT_FAILURE);
|
return finish("Could not load model", EXIT_FAILURE);
|
||||||
} else {
|
|
||||||
return finish("Could not load model", EXIT_FAILURE);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// save model
|
// save model
|
||||||
if (type2 == typeXML) {
|
if (type2 == typeXML) {
|
||||||
if (!mj_saveLastXML(argv[2], m, error, 1000)) {
|
if (!mj_saveLastXML(argv[2], m, error, 1000)) {
|
||||||
|
if (s) mj_deleteSpec(s);
|
||||||
return finish(error, EXIT_FAILURE, m);
|
return finish(error, EXIT_FAILURE, m);
|
||||||
}
|
}
|
||||||
} else if (type2 == typeMJB) {
|
} else if (type2 == typeMJB) {
|
||||||
@@ -174,15 +200,6 @@ int main(int argc, char** argv) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// finalize
|
// finalize
|
||||||
char msg[1000];
|
if (s) mj_deleteSpec(s);
|
||||||
if (first && type2 == typeNONE) {
|
return finish("\nDone.", EXIT_SUCCESS, m);
|
||||||
snprintf(msg, sizeof(msg), "Done.\n"
|
|
||||||
"First compile: %.4gs\n"
|
|
||||||
"Second compile: %.4gs",
|
|
||||||
first, second);
|
|
||||||
} else {
|
|
||||||
snprintf(msg, sizeof(msg), "Done.");
|
|
||||||
}
|
|
||||||
|
|
||||||
return finish(msg, EXIT_SUCCESS, m);
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -427,6 +427,17 @@ const char* mjs_getError(mjSpec* s) {
|
|||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
// get compiler timers from model
|
||||||
|
const double* mjs_getTimer(mjSpec* s) {
|
||||||
|
if (!s) {
|
||||||
|
return nullptr;
|
||||||
|
}
|
||||||
|
mjCModel* modelC = static_cast<mjCModel*>(s->element);
|
||||||
|
return modelC->timer;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
// check if model has warnings
|
// check if model has warnings
|
||||||
int mjs_isWarning(mjSpec* s) {
|
int mjs_isWarning(mjSpec* s) {
|
||||||
mjCModel* modelC = static_cast<mjCModel*>(s->element);
|
mjCModel* modelC = static_cast<mjCModel*>(s->element);
|
||||||
|
|||||||
+25
-4
@@ -14,19 +14,18 @@
|
|||||||
|
|
||||||
#include <algorithm>
|
#include <algorithm>
|
||||||
#include <array>
|
#include <array>
|
||||||
|
#include <chrono>
|
||||||
#include <climits>
|
#include <climits>
|
||||||
#include <cmath>
|
#include <cmath>
|
||||||
#include <csetjmp>
|
#include <csetjmp>
|
||||||
#include <cstddef>
|
#include <cstddef>
|
||||||
#include <cstdio>
|
#include <cstdio>
|
||||||
#include <cstring>
|
#include <cstring>
|
||||||
#include <deque>
|
|
||||||
#include <functional>
|
#include <functional>
|
||||||
#include <limits>
|
#include <limits>
|
||||||
#include <map>
|
#include <map>
|
||||||
#include <memory>
|
#include <memory>
|
||||||
#include <queue>
|
#include <queue>
|
||||||
#include <set>
|
|
||||||
#include <string>
|
#include <string>
|
||||||
#include <string_view>
|
#include <string_view>
|
||||||
#include <unordered_map>
|
#include <unordered_map>
|
||||||
@@ -688,11 +687,17 @@ void mjCMesh::Compile(const mjVFS* vfs) {
|
|||||||
|
|
||||||
// compiler
|
// compiler
|
||||||
void mjCMesh::TryCompile(const mjVFS* vfs) {
|
void mjCMesh::TryCompile(const mjVFS* vfs) {
|
||||||
|
using Clock = std::chrono::steady_clock;
|
||||||
|
using Seconds = std::chrono::duration<double>;
|
||||||
|
std::fill_n(mesh_timer_, mjNCTIMER, 0.0);
|
||||||
|
|
||||||
bool fromCache = false;
|
bool fromCache = false;
|
||||||
CopyFromSpec();
|
CopyFromSpec();
|
||||||
visual_ = true;
|
visual_ = true;
|
||||||
mjCCache *cache = reinterpret_cast<mjCCache*>(mj_getCache()->impl_);
|
mjCCache *cache = reinterpret_cast<mjCCache*>(mj_getCache()->impl_);
|
||||||
|
|
||||||
|
Clock::time_point t0 = Clock::now();
|
||||||
|
|
||||||
// load file
|
// load file
|
||||||
if (!file_.empty()) {
|
if (!file_.empty()) {
|
||||||
vert_.clear();
|
vert_.clear();
|
||||||
@@ -765,12 +770,13 @@ void mjCMesh::TryCompile(const mjVFS* vfs) {
|
|||||||
LoadSDF(); // create using marching cubes
|
LoadSDF(); // create using marching cubes
|
||||||
}
|
}
|
||||||
|
|
||||||
|
mesh_timer_[mjCTIMER_MESH_LOAD] = Seconds(Clock::now() - t0).count();
|
||||||
|
|
||||||
CheckInitialMesh();
|
CheckInitialMesh();
|
||||||
|
|
||||||
// compute mesh properties
|
// compute mesh properties
|
||||||
if (!fromCache) {
|
if (!fromCache) {
|
||||||
Process();
|
Process();
|
||||||
|
|
||||||
if (!file_.empty()) {
|
if (!file_.empty()) {
|
||||||
CacheMesh(cache, resource_);
|
CacheMesh(cache, resource_);
|
||||||
}
|
}
|
||||||
@@ -778,6 +784,7 @@ void mjCMesh::TryCompile(const mjVFS* vfs) {
|
|||||||
// When a mesh is loaded from the cache, has no octree but needs one,
|
// When a mesh is loaded from the cache, has no octree but needs one,
|
||||||
// we need to compute it here. If inversely it has an octree but we *do not*
|
// we need to compute it here. If inversely it has an octree but we *do not*
|
||||||
// need one, we clear it.
|
// need one, we clear it.
|
||||||
|
t0 = Clock::now();
|
||||||
if (!needsdf) {
|
if (!needsdf) {
|
||||||
octree_.Clear();
|
octree_.Clear();
|
||||||
} else if (octree_.NumNodes() == 0) {
|
} else if (octree_.NumNodes() == 0) {
|
||||||
@@ -789,6 +796,7 @@ void mjCMesh::TryCompile(const mjVFS* vfs) {
|
|||||||
octree_.ComputeSdfCoeffs(dvert.data(), nvert(), face_.data(), nface(), tree_);
|
octree_.ComputeSdfCoeffs(dvert.data(), nvert(), face_.data(), nface(), tree_);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
mesh_timer_[mjCTIMER_MESH_OCTREE] = Seconds(Clock::now() - t0).count();
|
||||||
}
|
}
|
||||||
|
|
||||||
// close resource
|
// close resource
|
||||||
@@ -1341,7 +1349,9 @@ double mjCMesh::ComputeFaceCentroid(double facecen[3], const double* dvert) cons
|
|||||||
|
|
||||||
void mjCMesh::Process() {
|
void mjCMesh::Process() {
|
||||||
std::vector<double> dvert(vert_.begin(), vert_.end());
|
std::vector<double> dvert(vert_.begin(), vert_.end());
|
||||||
|
using Clock = std::chrono::steady_clock;
|
||||||
|
using Seconds = std::chrono::duration<double>;
|
||||||
|
Clock::time_point t0;
|
||||||
// create half-edge structure (if mesh was in XML)
|
// create half-edge structure (if mesh was in XML)
|
||||||
if (halfedge_.empty()) {
|
if (halfedge_.empty()) {
|
||||||
for (int i = 0; i < nface(); i++) {
|
for (int i = 0; i < nface(); i++) {
|
||||||
@@ -1370,6 +1380,7 @@ void mjCMesh::Process() {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
t0 = Clock::now();
|
||||||
// make graph describing convex hull
|
// make graph describing convex hull
|
||||||
if (needhull_ || face_.empty()) {
|
if (needhull_ || face_.empty()) {
|
||||||
MakeGraph(dvert.data());
|
MakeGraph(dvert.data());
|
||||||
@@ -1379,7 +1390,9 @@ void mjCMesh::Process() {
|
|||||||
if (face_.empty()) {
|
if (face_.empty()) {
|
||||||
CopyGraph();
|
CopyGraph();
|
||||||
}
|
}
|
||||||
|
mesh_timer_[mjCTIMER_MESH_HULL] += Seconds(Clock::now() - t0).count();
|
||||||
|
|
||||||
|
t0 = Clock::now();
|
||||||
// no normals: make
|
// no normals: make
|
||||||
if (normal_.empty()) {
|
if (normal_.empty()) {
|
||||||
MakeNormal(dvert.data());
|
MakeNormal(dvert.data());
|
||||||
@@ -1424,6 +1437,9 @@ void mjCMesh::Process() {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
mesh_timer_[mjCTIMER_MESH_POLYGON] += Seconds(Clock::now() - t0).count();
|
||||||
|
|
||||||
|
t0 = Clock::now();
|
||||||
// user offset, rotation, scaling
|
// user offset, rotation, scaling
|
||||||
ApplyTransformations(dvert.data());
|
ApplyTransformations(dvert.data());
|
||||||
|
|
||||||
@@ -1519,7 +1535,9 @@ void mjCMesh::Process() {
|
|||||||
|
|
||||||
// recompute polygon normals
|
// recompute polygon normals
|
||||||
MakePolygonNormals(dvert.data());
|
MakePolygonNormals(dvert.data());
|
||||||
|
mesh_timer_[mjCTIMER_MESH_INERTIA] += Seconds(Clock::now() - t0).count();
|
||||||
|
|
||||||
|
t0 = Clock::now();
|
||||||
// make bounding volume hierarchy
|
// make bounding volume hierarchy
|
||||||
if (tree_.Bvh().empty()) {
|
if (tree_.Bvh().empty()) {
|
||||||
face_aabb_.clear();
|
face_aabb_.clear();
|
||||||
@@ -1530,7 +1548,9 @@ void mjCMesh::Process() {
|
|||||||
}
|
}
|
||||||
tree_.CreateBVH();
|
tree_.CreateBVH();
|
||||||
}
|
}
|
||||||
|
mesh_timer_[mjCTIMER_MESH_BVH] += Seconds(Clock::now() - t0).count();
|
||||||
|
|
||||||
|
t0 = Clock::now();
|
||||||
// make octree
|
// make octree
|
||||||
if (needsdf) {
|
if (needsdf) {
|
||||||
octree_.SetFace(dvert, face_);
|
octree_.SetFace(dvert, face_);
|
||||||
@@ -1546,6 +1566,7 @@ void mjCMesh::Process() {
|
|||||||
for (int i = 0; i < (int)dvert.size(); i++) {
|
for (int i = 0; i < (int)dvert.size(); i++) {
|
||||||
vert_[i] = (float)dvert[i];
|
vert_[i] = (float)dvert[i];
|
||||||
}
|
}
|
||||||
|
mesh_timer_[mjCTIMER_MESH_OCTREE] += Seconds(Clock::now() - t0).count();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
+30
-6
@@ -16,7 +16,7 @@
|
|||||||
|
|
||||||
#include <algorithm>
|
#include <algorithm>
|
||||||
#include <array>
|
#include <array>
|
||||||
#include <atomic>
|
#include <chrono>
|
||||||
#include <cmath>
|
#include <cmath>
|
||||||
#include <csetjmp>
|
#include <csetjmp>
|
||||||
#include <cstdint>
|
#include <cstdint>
|
||||||
@@ -662,7 +662,6 @@ mjCModel& mjCModel::operator+=(mjCDef& subtree) {
|
|||||||
|
|
||||||
// remove default class from array
|
// remove default class from array
|
||||||
mjCModel& mjCModel::operator-=(const mjCDef& subtree) {
|
mjCModel& mjCModel::operator-=(const mjCDef& subtree) {
|
||||||
|
|
||||||
// check we aren't trying to remove the 'main' default
|
// check we aren't trying to remove the 'main' default
|
||||||
if (subtree.id == 0) {
|
if (subtree.id == 0) {
|
||||||
throw mjCError(0, "cannot remove the global default ('main')");
|
throw mjCError(0, "cannot remove the global default ('main')");
|
||||||
@@ -922,7 +921,7 @@ void mjCModel::ComputeSparseSizes() {
|
|||||||
|
|
||||||
// 1. build dof_parentid, dof_bodyid
|
// 1. build dof_parentid, dof_bodyid
|
||||||
if (nbody > 0) {
|
if (nbody > 0) {
|
||||||
body_lastdof_map[0] = -1; // world has no parent dof
|
body_lastdof_map[0] = -1; // world has no parent dof
|
||||||
}
|
}
|
||||||
for (int i = 0; i < nbody; ++i) {
|
for (int i = 0; i < nbody; ++i) {
|
||||||
mjCBody* pb = bodies_[i];
|
mjCBody* pb = bodies_[i];
|
||||||
@@ -961,7 +960,7 @@ void mjCModel::ComputeSparseSizes() {
|
|||||||
nD = 2 * nM - nv;
|
nD = 2 * nM - nv;
|
||||||
|
|
||||||
// 4. compute subtreedofs and nB
|
// 4. compute subtreedofs and nB
|
||||||
for(int i = nbody - 1; i >= 0; --i) {
|
for (int i = nbody - 1; i >= 0; --i) {
|
||||||
bodies_[i]->subtreedofs = bodies_[i]->dofnum;
|
bodies_[i]->subtreedofs = bodies_[i]->dofnum;
|
||||||
for (const auto* child : bodies_[i]->Bodies()) {
|
for (const auto* child : bodies_[i]->Bodies()) {
|
||||||
bodies_[i]->subtreedofs += child->subtreedofs;
|
bodies_[i]->subtreedofs += child->subtreedofs;
|
||||||
@@ -984,7 +983,7 @@ void mjCModel::ComputeSparseSizes() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// 5. compute nC
|
// 5. compute nC
|
||||||
for(int i = 0; i < nbody; ++i) {
|
for (int i = 0; i < nbody; ++i) {
|
||||||
mjCBody* pb = bodies_[i];
|
mjCBody* pb = bodies_[i];
|
||||||
mjCBody* par = pb->parent;
|
mjCBody* par = pb->parent;
|
||||||
int parentid = par ? par->id : 0;
|
int parentid = par ? par->id : 0;
|
||||||
@@ -4744,10 +4743,13 @@ static void CompileMesh(mjCMesh* mesh, const mjVFS* vfs,
|
|||||||
static void CompileTexture(mjCTexture* texture, const mjVFS* vfs,
|
static void CompileTexture(mjCTexture* texture, const mjVFS* vfs,
|
||||||
std::exception_ptr& exception,
|
std::exception_ptr& exception,
|
||||||
std::mutex& exception_mutex, std::string* warningtext) {
|
std::mutex& exception_mutex, std::string* warningtext) {
|
||||||
|
using Clock = std::chrono::steady_clock;
|
||||||
|
using Seconds = std::chrono::duration<double>;
|
||||||
local_warningtext_ptr = warningtext;
|
local_warningtext_ptr = warningtext;
|
||||||
auto previous_handler = _mjPRIVATE__get_tls_warning_fn();
|
auto previous_handler = _mjPRIVATE__get_tls_warning_fn();
|
||||||
_mjPRIVATE__set_tls_warning_fn(warninghandler);
|
_mjPRIVATE__set_tls_warning_fn(warninghandler);
|
||||||
|
|
||||||
|
Clock::time_point t0 = Clock::now();
|
||||||
try {
|
try {
|
||||||
texture->Compile(vfs);
|
texture->Compile(vfs);
|
||||||
} catch (...) {
|
} catch (...) {
|
||||||
@@ -4756,6 +4758,7 @@ static void CompileTexture(mjCTexture* texture, const mjVFS* vfs,
|
|||||||
exception = std::current_exception();
|
exception = std::current_exception();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
texture->texture_time_ = Seconds(Clock::now() - t0).count();
|
||||||
|
|
||||||
_mjPRIVATE__set_tls_warning_fn(previous_handler);
|
_mjPRIVATE__set_tls_warning_fn(previous_handler);
|
||||||
local_warningtext_ptr = nullptr;
|
local_warningtext_ptr = nullptr;
|
||||||
@@ -4843,6 +4846,15 @@ void mjCModel::CompileMeshesAndTextures(const mjVFS* vfs) {
|
|||||||
if (texture_exception) {
|
if (texture_exception) {
|
||||||
std::rethrow_exception(texture_exception);
|
std::rethrow_exception(texture_exception);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
for (int i = 0; i < nmesh; i++) {
|
||||||
|
for (int t = 0; t < mjNCTIMER; t++) {
|
||||||
|
timer[t] += meshes_[i]->mesh_timer_[t];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
for (int i = 0; i < ntexture; i++) {
|
||||||
|
timer[mjCTIMER_TEXTURE] += textures_[i]->texture_time_;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// compute qpos0
|
// compute qpos0
|
||||||
@@ -4984,6 +4996,12 @@ void mjCModel::TryCompile(mjModel*& m, mjData*& d, const mjVFS* vfs) {
|
|||||||
} disable_usethread(compiler.usethread);
|
} disable_usethread(compiler.usethread);
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
using Clock = std::chrono::steady_clock;
|
||||||
|
using Seconds = std::chrono::duration<double>;
|
||||||
|
for (int i=0; i < mjNCTIMER; i++) {
|
||||||
|
timer[i] = 0;
|
||||||
|
}
|
||||||
|
Clock::time_point timer_start = Clock::now();
|
||||||
// check if nan test works
|
// check if nan test works
|
||||||
double test = mjNAN;
|
double test = mjNAN;
|
||||||
if (mjuu_defined(test)) {
|
if (mjuu_defined(test)) {
|
||||||
@@ -5085,7 +5103,11 @@ void mjCModel::TryCompile(mjModel*& m, mjData*& d, const mjVFS* vfs) {
|
|||||||
SetNuser();
|
SetNuser();
|
||||||
|
|
||||||
// compile meshes and textures (needed for geom compilation)
|
// compile meshes and textures (needed for geom compilation)
|
||||||
CompileMeshesAndTextures(vfs);
|
{
|
||||||
|
Clock::time_point t0 = Clock::now();
|
||||||
|
CompileMeshesAndTextures(vfs);
|
||||||
|
timer[mjCTIMER_ASSETS] = Seconds(Clock::now() - t0).count();
|
||||||
|
}
|
||||||
|
|
||||||
// compile objects in kinematic tree
|
// compile objects in kinematic tree
|
||||||
for (int i=0; i < bodies_.size(); i++) {
|
for (int i=0; i < bodies_.size(); i++) {
|
||||||
@@ -5359,6 +5381,8 @@ void mjCModel::TryCompile(mjModel*& m, mjData*& d, const mjVFS* vfs) {
|
|||||||
// save signature
|
// save signature
|
||||||
m->signature = Signature();
|
m->signature = Signature();
|
||||||
|
|
||||||
|
timer[mjCTIMER_TOTAL] = Seconds(Clock::now() - timer_start).count();
|
||||||
|
|
||||||
// special cases that are not caused by user edits
|
// special cases that are not caused by user edits
|
||||||
if (compiler.fusestatic || compiler.discardvisual ||
|
if (compiler.fusestatic || compiler.discardvisual ||
|
||||||
!pairs_.empty() || !excludes_.empty()) {
|
!pairs_.empty() || !excludes_.empty()) {
|
||||||
|
|||||||
@@ -198,6 +198,7 @@ class mjCModel : public mjCModel_, private mjSpec {
|
|||||||
mjCModel& operator-=(const mjCDef& subtree); // remove default tree from this model
|
mjCModel& operator-=(const mjCDef& subtree); // remove default tree from this model
|
||||||
|
|
||||||
mjSpec spec;
|
mjSpec spec;
|
||||||
|
double timer[mjNCTIMER] = {0}; // compiler timers
|
||||||
|
|
||||||
mjModel* Compile(const mjVFS* vfs = nullptr, mjModel** m = nullptr); // construct mjModel
|
mjModel* Compile(const mjVFS* vfs = nullptr, mjModel** m = nullptr); // construct mjModel
|
||||||
bool CopyBack(const mjModel*); // DECOMPILER: copy numeric back
|
bool CopyBack(const mjModel*); // DECOMPILER: copy numeric back
|
||||||
|
|||||||
@@ -1133,6 +1133,7 @@ class mjCMesh_ : public mjCBase {
|
|||||||
|
|
||||||
// octree
|
// octree
|
||||||
mjCOctree octree_; // octree of the mesh
|
mjCOctree octree_; // octree of the mesh
|
||||||
|
double mesh_timer_[mjNCTIMER] = {0};
|
||||||
};
|
};
|
||||||
|
|
||||||
class mjCMesh: public mjCMesh_, private mjsMesh {
|
class mjCMesh: public mjCMesh_, private mjsMesh {
|
||||||
@@ -1465,6 +1466,7 @@ class mjCTexture : public mjCTexture_, private mjsTexture {
|
|||||||
void PointToLocal(void);
|
void PointToLocal(void);
|
||||||
void NameSpace(const mjCModel* m);
|
void NameSpace(const mjCModel* m);
|
||||||
void Compile(const mjVFS* vfs);
|
void Compile(const mjVFS* vfs);
|
||||||
|
double texture_time_ = 0;
|
||||||
|
|
||||||
std::string File() const { return file_; }
|
std::string File() const { return file_; }
|
||||||
std::string get_content_type() const { return content_type_; }
|
std::string get_content_type() const { return content_type_; }
|
||||||
|
|||||||
@@ -3378,5 +3378,32 @@ TEST_F(MujocoTest, UserValue) {
|
|||||||
mj_deleteSpec(spec);
|
mj_deleteSpec(spec);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
TEST_F(MujocoTest, CompilerTimers) {
|
||||||
|
static constexpr char xml[] = R"(
|
||||||
|
<mujoco>
|
||||||
|
<asset>
|
||||||
|
<texture name="grid" type="2d" builtin="checker" width="300" height="300" rgb1=".1 .2 .3" rgb2=".2 .3 .4"/>
|
||||||
|
<material name="grid" texture="grid"/>
|
||||||
|
</asset>
|
||||||
|
<worldbody>
|
||||||
|
<geom type="plane" size="1 1 1" material="grid"/>
|
||||||
|
</worldbody>
|
||||||
|
</mujoco>
|
||||||
|
)";
|
||||||
|
std::array<char, 1024> error;
|
||||||
|
mjSpec* spec = mj_parseXMLString(xml, 0, error.data(), error.size());
|
||||||
|
ASSERT_THAT(spec, NotNull()) << error.data();
|
||||||
|
|
||||||
|
mjModel* model = mj_compile(spec, 0);
|
||||||
|
ASSERT_THAT(model, NotNull());
|
||||||
|
|
||||||
|
EXPECT_GT(mjs_getTimer(spec)[mjCTIMER_TOTAL], 0);
|
||||||
|
EXPECT_GT(mjs_getTimer(spec)[mjCTIMER_ASSETS], 0);
|
||||||
|
EXPECT_GT(mjs_getTimer(spec)[mjCTIMER_TEXTURE], 0);
|
||||||
|
|
||||||
|
mj_deleteModel(model);
|
||||||
|
mj_deleteSpec(spec);
|
||||||
|
}
|
||||||
|
|
||||||
} // namespace
|
} // namespace
|
||||||
} // namespace mujoco
|
} // namespace mujoco
|
||||||
|
|||||||
@@ -573,6 +573,18 @@ public enum mjtOrientation : int{
|
|||||||
mjORIENTATION_ZAXIS = 3,
|
mjORIENTATION_ZAXIS = 3,
|
||||||
mjORIENTATION_EULER = 4,
|
mjORIENTATION_EULER = 4,
|
||||||
}
|
}
|
||||||
|
public enum mjtCTimer : int{
|
||||||
|
mjCTIMER_TOTAL = 0,
|
||||||
|
mjCTIMER_ASSETS = 1,
|
||||||
|
mjCTIMER_TEXTURE = 2,
|
||||||
|
mjCTIMER_MESH_LOAD = 3,
|
||||||
|
mjCTIMER_MESH_HULL = 4,
|
||||||
|
mjCTIMER_MESH_POLYGON = 5,
|
||||||
|
mjCTIMER_MESH_INERTIA = 6,
|
||||||
|
mjCTIMER_MESH_BVH = 7,
|
||||||
|
mjCTIMER_MESH_OCTREE = 8,
|
||||||
|
mjNCTIMER = 9,
|
||||||
|
}
|
||||||
public enum mjtCatBit : int{
|
public enum mjtCatBit : int{
|
||||||
mjCAT_STATIC = 1,
|
mjCAT_STATIC = 1,
|
||||||
mjCAT_DYNAMIC = 2,
|
mjCAT_DYNAMIC = 2,
|
||||||
|
|||||||
@@ -5612,6 +5612,7 @@ struct MjSpec {
|
|||||||
std::unique_ptr<MjSpec> copy();
|
std::unique_ptr<MjSpec> copy();
|
||||||
mjSpec* get() const;
|
mjSpec* get() const;
|
||||||
void set(mjSpec* ptr);
|
void set(mjSpec* ptr);
|
||||||
|
emscripten::val timer() const;
|
||||||
mjString modelname() const {
|
mjString modelname() const {
|
||||||
return (ptr_ && ptr_->modelname) ? *(ptr_->modelname) : "";
|
return (ptr_ && ptr_->modelname) ? *(ptr_->modelname) : "";
|
||||||
}
|
}
|
||||||
@@ -8402,6 +8403,10 @@ MjSpec::~MjSpec() {
|
|||||||
mjSpec *MjSpec::get() const { return ptr_; }
|
mjSpec *MjSpec::get() const { return ptr_; }
|
||||||
void MjSpec::set(mjSpec *ptr) { ptr_ = ptr; }
|
void MjSpec::set(mjSpec *ptr) { ptr_ = ptr; }
|
||||||
|
|
||||||
|
emscripten::val MjSpec::timer() const {
|
||||||
|
return emscripten::val(emscripten::typed_memory_view(9, mjs_getTimer(ptr_)));
|
||||||
|
}
|
||||||
|
|
||||||
std::unique_ptr<MjModel> mj_loadXML_wrapper_1(std::string filename) {
|
std::unique_ptr<MjModel> mj_loadXML_wrapper_1(std::string filename) {
|
||||||
char error[1000];
|
char error[1000];
|
||||||
mjModel *model = mj_loadXML(filename.c_str(), nullptr, error, sizeof(error));
|
mjModel *model = mj_loadXML(filename.c_str(), nullptr, error, sizeof(error));
|
||||||
@@ -10911,6 +10916,17 @@ EMSCRIPTEN_BINDINGS(mujoco_bindings) {
|
|||||||
.value("mjBUTTON_LEFT", mjBUTTON_LEFT)
|
.value("mjBUTTON_LEFT", mjBUTTON_LEFT)
|
||||||
.value("mjBUTTON_RIGHT", mjBUTTON_RIGHT)
|
.value("mjBUTTON_RIGHT", mjBUTTON_RIGHT)
|
||||||
.value("mjBUTTON_MIDDLE", mjBUTTON_MIDDLE);
|
.value("mjBUTTON_MIDDLE", mjBUTTON_MIDDLE);
|
||||||
|
enum_<mjtCTimer>("mjtCTimer")
|
||||||
|
.value("mjCTIMER_TOTAL", mjCTIMER_TOTAL)
|
||||||
|
.value("mjCTIMER_ASSETS", mjCTIMER_ASSETS)
|
||||||
|
.value("mjCTIMER_TEXTURE", mjCTIMER_TEXTURE)
|
||||||
|
.value("mjCTIMER_MESH_LOAD", mjCTIMER_MESH_LOAD)
|
||||||
|
.value("mjCTIMER_MESH_HULL", mjCTIMER_MESH_HULL)
|
||||||
|
.value("mjCTIMER_MESH_POLYGON", mjCTIMER_MESH_POLYGON)
|
||||||
|
.value("mjCTIMER_MESH_INERTIA", mjCTIMER_MESH_INERTIA)
|
||||||
|
.value("mjCTIMER_MESH_BVH", mjCTIMER_MESH_BVH)
|
||||||
|
.value("mjCTIMER_MESH_OCTREE", mjCTIMER_MESH_OCTREE)
|
||||||
|
.value("mjNCTIMER", mjNCTIMER);
|
||||||
enum_<mjtCamLight>("mjtCamLight")
|
enum_<mjtCamLight>("mjtCamLight")
|
||||||
.value("mjCAMLIGHT_FIXED", mjCAMLIGHT_FIXED)
|
.value("mjCAMLIGHT_FIXED", mjCAMLIGHT_FIXED)
|
||||||
.value("mjCAMLIGHT_TRACK", mjCAMLIGHT_TRACK)
|
.value("mjCAMLIGHT_TRACK", mjCAMLIGHT_TRACK)
|
||||||
@@ -12371,6 +12387,7 @@ EMSCRIPTEN_BINDINGS(mujoco_bindings) {
|
|||||||
.property("nupdate", &MjSolverStat::nupdate, &MjSolverStat::set_nupdate, reference());
|
.property("nupdate", &MjSolverStat::nupdate, &MjSolverStat::set_nupdate, reference());
|
||||||
emscripten::class_<MjSpec>("MjSpec")
|
emscripten::class_<MjSpec>("MjSpec")
|
||||||
.constructor<const MjSpec &>()
|
.constructor<const MjSpec &>()
|
||||||
|
.property("timer", &MjSpec::timer)
|
||||||
.property("comment", &MjSpec::comment, &MjSpec::set_comment, reference())
|
.property("comment", &MjSpec::comment, &MjSpec::set_comment, reference())
|
||||||
.property("compiler", &MjSpec::compiler, reference())
|
.property("compiler", &MjSpec::compiler, reference())
|
||||||
.property("element", &MjSpec::element, reference())
|
.property("element", &MjSpec::element, reference())
|
||||||
|
|||||||
@@ -176,6 +176,7 @@ _SKIPPED_GETTERS_AND_SETTERS: tuple[str, ...] = (
|
|||||||
"mjs_getDouble",
|
"mjs_getDouble",
|
||||||
"mjs_getPluginAttributes",
|
"mjs_getPluginAttributes",
|
||||||
"mjs_getString",
|
"mjs_getString",
|
||||||
|
"mjs_getTimer",
|
||||||
"mjs_getUserValue",
|
"mjs_getUserValue",
|
||||||
"mjs_setBuffer",
|
"mjs_setBuffer",
|
||||||
"mjs_setDouble",
|
"mjs_setDouble",
|
||||||
|
|||||||
@@ -381,6 +381,9 @@ def build_struct_header(
|
|||||||
builder.line(f"{s}* get() const;")
|
builder.line(f"{s}* get() const;")
|
||||||
builder.line(f"void set({s}* ptr);")
|
builder.line(f"void set({s}* ptr);")
|
||||||
|
|
||||||
|
if w == "MjSpec":
|
||||||
|
builder.line("emscripten::val timer() const;")
|
||||||
|
|
||||||
# field declarations
|
# field declarations
|
||||||
for field in wrapped_fields:
|
for field in wrapped_fields:
|
||||||
if field.declaration and field not in member_inits:
|
if field.declaration and field not in member_inits:
|
||||||
@@ -598,6 +601,7 @@ def _build_struct_bindings(
|
|||||||
#undef X_ACCESSOR""".lstrip())
|
#undef X_ACCESSOR""".lstrip())
|
||||||
elif w == "MjSpec":
|
elif w == "MjSpec":
|
||||||
builder.line(".constructor<const MjSpec &>()")
|
builder.line(".constructor<const MjSpec &>()")
|
||||||
|
builder.line('.property("timer", &MjSpec::timer)')
|
||||||
elif w == "MjvScene":
|
elif w == "MjvScene":
|
||||||
builder.line(".constructor<MjModel *, int>()")
|
builder.line(".constructor<MjModel *, int>()")
|
||||||
builder.line(".constructor<>()")
|
builder.line(".constructor<>()")
|
||||||
|
|||||||
@@ -715,6 +715,10 @@ MjSpec::~MjSpec() {
|
|||||||
mjSpec *MjSpec::get() const { return ptr_; }
|
mjSpec *MjSpec::get() const { return ptr_; }
|
||||||
void MjSpec::set(mjSpec *ptr) { ptr_ = ptr; }
|
void MjSpec::set(mjSpec *ptr) { ptr_ = ptr; }
|
||||||
|
|
||||||
|
emscripten::val MjSpec::timer() const {
|
||||||
|
return emscripten::val(emscripten::typed_memory_view(9, mjs_getTimer(ptr_)));
|
||||||
|
}
|
||||||
|
|
||||||
std::unique_ptr<MjModel> mj_loadXML_wrapper_1(std::string filename) {
|
std::unique_ptr<MjModel> mj_loadXML_wrapper_1(std::string filename) {
|
||||||
char error[1000];
|
char error[1000];
|
||||||
mjModel *model = mj_loadXML(filename.c_str(), nullptr, error, sizeof(error));
|
mjModel *model = mj_loadXML(filename.c_str(), nullptr, error, sizeof(error));
|
||||||
|
|||||||
Reference in New Issue
Block a user