diff --git a/python/mujoco/render.cc b/python/mujoco/render.cc index fd7b9ffd..22699f24 100644 --- a/python/mujoco/render.cc +++ b/python/mujoco/render.cc @@ -37,6 +37,8 @@ class MjWrapper : public WrapperBase { MjWrapper(MjWrapper&&) = default; ~MjWrapper() = default; + void Free(); + #define X(var) py_array_or_tuple_t var X(fogRGBA); X(auxWidth); @@ -121,6 +123,12 @@ MjrContextWrapper::MjWrapper(const MjModelWrapper& model, int fontscale) X(charWidthBig) {} #undef X_SKIN #undef X + +void MjrContextWrapper::Free() { + // mjr_freeContext is safe to call multiple times. + InterceptMjErrors(mjr_freeContext)(ptr_); +} + } // namespace _impl namespace { @@ -156,6 +164,10 @@ PYBIND11_MODULE(_render, pymodule) { py::class_ mjrContext(pymodule, "MjrContext"); mjrContext.def(py::init<>()); mjrContext.def(py::init()); + mjrContext.def( + "free", [](MjrContextWrapper& self) { self.Free(); }, + py::doc("Frees resources in current active OpenGL context, sets struct " + "to default.")); #define X(var) \ mjrContext.def_property( \ #var, [](const MjrContextWrapper& c) { return c.get()->var; }, \ diff --git a/python/mujoco/render_test.py b/python/mujoco/render_test.py index 4c2f1b61..5f27a7a8 100644 --- a/python/mujoco/render_test.py +++ b/python/mujoco/render_test.py @@ -49,13 +49,10 @@ class MuJoCoRenderTest(absltest.TestCase): scene = mujoco.MjvScene(self.model, maxgeom=0) mujoco.mjv_updateScene( self.model, self.data, mujoco.MjvOption(), mujoco.MjvPerturb(), - mujoco.MjvCamera(), mujoco.mjtCatBit.mjCAT_ALL.value, scene) + mujoco.MjvCamera(), mujoco.mjtCatBit.mjCAT_ALL, scene) - context = mujoco.MjrContext( - self.model, - mujoco.mjtFontScale.mjFONTSCALE_150.value) - mujoco.mjr_setBuffer( - mujoco.mjtFramebuffer.mjFB_OFFSCREEN.value, context) + context = mujoco.MjrContext(self.model, mujoco.mjtFontScale.mjFONTSCALE_150) + mujoco.mjr_setBuffer(mujoco.mjtFramebuffer.mjFB_OFFSCREEN, context) # MuJoCo's default render buffer size is 640x480. full_rect = mujoco.MjrRect(0, 0, 640, 480) @@ -76,6 +73,22 @@ class MuJoCoRenderTest(absltest.TestCase): mujoco.mjr_readPixels( np.reshape(upside_down_image, -1), None, full_rect, context) np.testing.assert_array_equal(upside_down_image, expected_upside_down_image) + context.free() + + def test_safe_to_free_context_twice(self): + self.model = mujoco.MjModel.from_xml_string('') + self.data = mujoco.MjData(self.model) + + scene = mujoco.MjvScene(self.model, maxgeom=0) + mujoco.mjv_updateScene( + self.model, self.data, mujoco.MjvOption(), None, + mujoco.MjvCamera(), mujoco.mjtCatBit.mjCAT_ALL, scene) + + context = mujoco.MjrContext(self.model, mujoco.mjtFontScale.mjFONTSCALE_150) + mujoco.mjr_setBuffer(mujoco.mjtFramebuffer.mjFB_OFFSCREEN, context) + + context.free() + context.free() if __name__ == '__main__': absltest.main() diff --git a/python/setup.py b/python/setup.py index 4cab1805..3350af46 100644 --- a/python/setup.py +++ b/python/setup.py @@ -30,7 +30,7 @@ from setuptools import find_packages from setuptools import setup from setuptools.command import build_ext -__version__ = '2.1.2.post1' +__version__ = '2.1.2.post2' MUJOCO_CMAKE = 'MUJOCO_CMAKE' MUJOCO_CMAKE_ARGS = 'MUJOCO_CMAKE_ARGS'