# Copyright 2017-2018 The dm_control Authors. # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. # You may obtain a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. # See the License for the specific language governing permissions and # limitations under the License. # ============================================================================ """Integration tests for rendering.""" import os import platform from absl.testing import absltest from absl.testing import parameterized from dm_control import _render from dm_control import mujoco from dm_control.mujoco.testing import decorators from dm_control.mujoco.testing import image_utils DEBUG_IMAGE_DIR = os.environ.get('TEST_UNDECLARED_OUTPUTS_DIR', absltest.get_default_test_tmpdir()) # TODO(nimrod): Reduce the tolerance if we can figure out why the tests # fail. _RMS_TOLERANCE = 80.0 # Context creation with GLFW is not threadsafe. if _render.BACKEND == 'glfw': # On Linux we are able to create a GLFW window in a single thread that is not # the main thread. # On Mac we are only allowed to create windows on the main thread, so we # disable the `run_threaded` wrapper entirely. NUM_THREADS = None if platform.system() == 'Darwin' else 1 else: NUM_THREADS = 4 CALLS_PER_THREAD = 1 class RenderTest(parameterized.TestCase): @parameterized.named_parameters(image_utils.SEQUENCES.items()) @image_utils.save_images_on_failure(output_dir=DEBUG_IMAGE_DIR) @decorators.run_threaded(num_threads=NUM_THREADS, calls_per_thread=CALLS_PER_THREAD) def test_render(self, sequence): for expected, actual in zip(sequence.iter_load(), sequence.iter_render()): image_utils.assert_images_close( expected, actual, tolerance=_RMS_TOLERANCE ) @decorators.run_threaded(num_threads=NUM_THREADS, calls_per_thread=CALLS_PER_THREAD) @image_utils.save_images_on_failure(output_dir=DEBUG_IMAGE_DIR) def test_render_multiple_physics_per_thread(self): cartpole = image_utils.cartpole humanoid = image_utils.humanoid cartpole_frames = [] humanoid_frames = [] for cartpole_frame, humanoid_frame in zip(cartpole.iter_render(), humanoid.iter_render()): cartpole_frames.append(cartpole_frame) humanoid_frames.append(humanoid_frame) for expected, actual in zip(cartpole.iter_load(), cartpole_frames): image_utils.assert_images_close( expected, actual, tolerance=_RMS_TOLERANCE ) for expected, actual in zip(humanoid.iter_load(), humanoid_frames): image_utils.assert_images_close( expected, actual, tolerance=_RMS_TOLERANCE ) @decorators.run_threaded(num_threads=NUM_THREADS, calls_per_thread=1) def test_repeatedly_create_and_destroy_rendering_contexts(self): # Tests for errors that may occur due to per-thread GL resource leakage. physics = mujoco.Physics.from_xml_string('') for _ in range(500): physics._make_rendering_contexts() physics._free_rendering_contexts() if __name__ == '__main__': absltest.main()