diff --git a/.github/workflows/generic.yml b/.github/workflows/generic.yml index 19c8b4ddbe..a7640e3e64 100644 --- a/.github/workflows/generic.yml +++ b/.github/workflows/generic.yml @@ -162,7 +162,7 @@ jobs: - name: Run unit tests run: | - pytest -v --logical --dev --backend ${{ matrix.GS_BACKEND }} -m required --forked ./tests + pytest -v --logical --dev --backend ${{ matrix.GS_BACKEND }} -m 'required and not slow' --forked ./tests - name: Save Updated Taichi Kernel Cache if: >- diff --git a/genesis/engine/materials/MPM/base.py b/genesis/engine/materials/MPM/base.py index 5896192f80..4f71deaf0b 100644 --- a/genesis/engine/materials/MPM/base.py +++ b/genesis/engine/materials/MPM/base.py @@ -1,3 +1,5 @@ +import platform + import gstaichi as ti import genesis as gs @@ -27,8 +29,8 @@ class Base(Material): mu: float, optional The second Lame's parameter. Default is None, computed by E and nu. sampler: str, optional - Particle sampler ('pbs', 'regular', 'random'). Note that 'pbs' is only supported on Linux for now. Defaults to - 'pbs' on supported platforms, 'random' otherwise. + Particle sampler ('pbs', 'regular', 'random'). Note that 'pbs' is only supported on Linux x86 for now. Defaults + to 'pbs' on supported platforms, 'random' otherwise. """ def __init__( @@ -46,7 +48,7 @@ def __init__( super().__init__() if sampler is None: - sampler = "pbs" if gs.platform == "Linux" else "random" + sampler = "pbs" if (gs.platform == "Linux" and platform.machine() == "x86_64") else "random" if not (sampler in ("pbs", "random", "regular") or sampler.startswith("pbs-")): gs.raise_exception( f"Particle sampler must be either 'pbs(-[0-9]+)', 'random' or 'regular. Got '{sampler}'." diff --git a/genesis/engine/materials/MPM/elastic.py b/genesis/engine/materials/MPM/elastic.py index b14081d308..c362c9126a 100644 --- a/genesis/engine/materials/MPM/elastic.py +++ b/genesis/engine/materials/MPM/elastic.py @@ -29,8 +29,8 @@ class Elastic(Base): mu: float, optional The second Lame's parameter. Default is None, computed by E and nu. sampler: str, optional - Particle sampler ('pbs', 'regular', 'random'). Note that 'pbs' is only supported on Linux for now. Defaults to - 'pbs' on supported platforms, 'random' otherwise. + Particle sampler ('pbs', 'regular', 'random'). Note that 'pbs' is only supported on Linux x86 for now. Defaults + to 'pbs' on supported platforms, 'random' otherwise. model: str, optional Stress model ('corotation', 'neohooken'). Default is 'corotation'. """ diff --git a/genesis/engine/materials/MPM/elasto_plastic.py b/genesis/engine/materials/MPM/elasto_plastic.py index be62b8de19..517ccb8046 100644 --- a/genesis/engine/materials/MPM/elasto_plastic.py +++ b/genesis/engine/materials/MPM/elasto_plastic.py @@ -28,8 +28,8 @@ class ElastoPlastic(Base): mu: float, optional The second Lame's parameter. Default is None, computed by E and nu. sampler: str, optional - Particle sampler ('pbs', 'regular', 'random'). Note that 'pbs' is only supported on Linux for now. Defaults to - 'pbs' on supported platforms, 'random' otherwise. + Particle sampler ('pbs', 'regular', 'random'). Note that 'pbs' is only supported on Linux x86 for now. Defaults + to 'pbs' on supported platforms, 'random' otherwise. yield_lower: float, optional Lower bound for the yield clamp (ignored if using von Mises). Default is 2.5e-2. yield_higher: float, optional diff --git a/genesis/engine/materials/MPM/liquid.py b/genesis/engine/materials/MPM/liquid.py index 0d2691ad2d..587f5b052a 100644 --- a/genesis/engine/materials/MPM/liquid.py +++ b/genesis/engine/materials/MPM/liquid.py @@ -23,8 +23,8 @@ class Liquid(Base): mu: float, optional The second Lame's parameter. Default is None, computed by E and nu. sampler: str, optional - Particle sampler ('pbs', 'regular', 'random'). Note that 'pbs' is only supported on Linux for now. Defaults to - 'pbs' on supported platforms, 'random' otherwise. + Particle sampler ('pbs', 'regular', 'random'). Note that 'pbs' is only supported on Linux x86 for now. Defaults + to 'pbs' on supported platforms, 'random' otherwise. viscous: str, bool Whether the liquid is viscous. Simply set mu to zero when non-viscuous. Default is False. """ diff --git a/genesis/engine/materials/MPM/muscle.py b/genesis/engine/materials/MPM/muscle.py index ca82b486c5..fa8de5bff6 100644 --- a/genesis/engine/materials/MPM/muscle.py +++ b/genesis/engine/materials/MPM/muscle.py @@ -23,8 +23,8 @@ class Muscle(Elastic): mu: float, optional The second Lame's parameter. Default is None, computed by E and nu. sampler: str, optional - Particle sampler ('pbs', 'regular', 'random'). Note that 'pbs' is only supported on Linux for now. Defaults to - 'pbs' on supported platforms, 'random' otherwise. + Particle sampler ('pbs', 'regular', 'random'). Note that 'pbs' is only supported on Linux x86 for now. Defaults + to 'pbs' on supported platforms, 'random' otherwise. model: str, optional Stress model ('corotation', 'neohooken'). Default is 'corotation'. n_groups: int, optional diff --git a/genesis/engine/materials/PBD/liquid.py b/genesis/engine/materials/PBD/liquid.py index 01d54d9933..5485bcf151 100644 --- a/genesis/engine/materials/PBD/liquid.py +++ b/genesis/engine/materials/PBD/liquid.py @@ -15,8 +15,8 @@ class Liquid(Base): rho: float, optional The rest density of the fluid in kg/m^3. Default is 1000.0. sampler: str, optional - Particle sampler ('pbs', 'regular', 'random'). Note that 'pbs' is only supported on Linux for now. Defaults to - 'pbs' on supported platforms, 'random' otherwise. + Particle sampler ('pbs', 'regular', 'random'). Note that 'pbs' is only supported on Linux x86 for now. Defaults + to 'pbs' on supported platforms, 'random' otherwise. density_relaxation: float, optional Relaxation factor for solving the density constraint. Controls the strength of positional correction to enforce incompressibility. Higher values lead to faster convergence but can cause instability. Default is 0.2. diff --git a/genesis/engine/materials/PBD/particle.py b/genesis/engine/materials/PBD/particle.py index 3be33241c0..7b79062ec1 100644 --- a/genesis/engine/materials/PBD/particle.py +++ b/genesis/engine/materials/PBD/particle.py @@ -21,8 +21,8 @@ class Particle(Base): rho: float, optional The rest density. Default is 1000.0. sampler: str, optional - Particle sampler ('pbs', 'regular', 'random'). Note that 'pbs' is only supported on Linux for now. Defaults to - 'pbs' on supported platforms, 'random' otherwise. + Particle sampler ('pbs', 'regular', 'random'). Note that 'pbs' is only supported on Linux x86 for now. Defaults + to 'pbs' on supported platforms, 'random' otherwise. """ def __init__( diff --git a/genesis/engine/materials/SPH/base.py b/genesis/engine/materials/SPH/base.py index d1089fa2fb..ef31f23832 100644 --- a/genesis/engine/materials/SPH/base.py +++ b/genesis/engine/materials/SPH/base.py @@ -17,8 +17,8 @@ class Base(Material): Parameters ---------- sampler: str, optional - Particle sampler ('pbs', 'regular', 'random'). Note that 'pbs' is only supported on Linux for now. Defaults to - 'pbs' on supported platforms, 'random' otherwise. + Particle sampler ('pbs', 'regular', 'random'). Note that 'pbs' is only supported on Linux x86 for now. Defaults + to 'pbs' on supported platforms, 'random' otherwise. """ def __init__( diff --git a/genesis/engine/materials/SPH/liquid.py b/genesis/engine/materials/SPH/liquid.py index a373b0c643..0e941be8ee 100644 --- a/genesis/engine/materials/SPH/liquid.py +++ b/genesis/engine/materials/SPH/liquid.py @@ -23,8 +23,8 @@ class Liquid(Base): gamma: float, optional The surface tension of the liquid. Controls how strongly the material "clumps" together at boundaries. Default is 0.01 sampler: str, optional - Particle sampler ('pbs', 'regular', 'random'). Note that 'pbs' is only supported on Linux for now. Defaults to - 'pbs' on supported platforms, 'random' otherwise. + Particle sampler ('pbs', 'regular', 'random'). Note that 'pbs' is only supported on Linux x86 for now. Defaults + to 'pbs' on supported platforms, 'random' otherwise. """ def __init__( diff --git a/genesis/engine/scene.py b/genesis/engine/scene.py index ad0b1da7ae..a12c3495de 100644 --- a/genesis/engine/scene.py +++ b/genesis/engine/scene.py @@ -360,10 +360,7 @@ def add_entity( gs.raise_exception(f"Unsupported material for morph: {material} and {morph}.") if surface.double_sided is None: - if isinstance(material, gs.materials.PBD.Cloth): - surface.double_sided = True - else: - surface.double_sided = False + surface.double_sided = isinstance(material, gs.materials.PBD.Cloth) if vis_mode is not None: surface.vis_mode = vis_mode @@ -374,7 +371,8 @@ def add_entity( if surface.vis_mode not in ("visual", "collision", "sdf"): gs.raise_exception( - f"Unsupported `surface.vis_mode` for material {material}: '{surface.vis_mode}'. Expected one of: ['visual', 'collision', 'sdf']." + f"Unsupported `surface.vis_mode` for material {material}: '{surface.vis_mode}'. Expected one of: " + "['visual', 'collision', 'sdf']." ) elif isinstance( diff --git a/genesis/ext/pyrender/scene.py b/genesis/ext/pyrender/scene.py index 9ee7ad3e77..0c1d788e42 100644 --- a/genesis/ext/pyrender/scene.py +++ b/genesis/ext/pyrender/scene.py @@ -481,8 +481,6 @@ def set_pose(self, node, pose): pose : (4,4) float The pose to set the node to. """ - # if node not in self.nodes: - # raise ValueError('Node must already be in scene') node._matrix = pose if node.mesh is not None: self._bounds = None diff --git a/tests/test_grad.py b/tests/test_grad.py index 98bcce8fab..9a646a795f 100644 --- a/tests/test_grad.py +++ b/tests/test_grad.py @@ -20,7 +20,6 @@ @pytest.mark.required -@pytest.mark.skipif(platform.machine() == "aarch64", reason="Physics-based particle sampler not supported on ARM.") @pytest.mark.parametrize("backend", [gs.cpu, gs.gpu]) def test_differentiable_push(show_viewer): HORIZON = 10 diff --git a/tests/test_hybrid.py b/tests/test_hybrid.py index 08522be829..47480923f7 100644 --- a/tests/test_hybrid.py +++ b/tests/test_hybrid.py @@ -83,8 +83,11 @@ def test_rigid_mpm_muscle(show_viewer): assert_allclose(ball_pos_delta[..., 2], 0.0, tol=1e-3) +@pytest.mark.slow # ~700s @pytest.mark.required def test_mesh_mpm_build(show_viewer): + # FIXME: This test is crashing on Linux (x86 & aarch64) Github-hosted runners + scene = gs.Scene( mpm_options=gs.options.MPMOptions( lower_bound=(-0.5, -0.5, -0.5), diff --git a/tests/test_mesh.py b/tests/test_mesh.py index a67dfca91a..0d691aa2f4 100644 --- a/tests/test_mesh.py +++ b/tests/test_mesh.py @@ -517,7 +517,7 @@ def test_splashsurf_surface_reconstruction(show_viewer): scene = gs.Scene( show_viewer=show_viewer, ) - water = scene.add_entity( + scene.add_entity( material=gs.materials.SPH.Liquid(), morph=gs.morphs.Box( pos=(0.15, 0.15, 0.22), diff --git a/tests/test_sensor_camera.py b/tests/test_sensor_camera.py index bdfa11e5fd..5a57a4c847 100644 --- a/tests/test_sensor_camera.py +++ b/tests/test_sensor_camera.py @@ -11,25 +11,39 @@ @pytest.mark.required @pytest.mark.parametrize("n_envs", [0, 1]) -def test_rasterizer_camera_sensor(show_viewer, tol, n_envs): +def test_rasterizer_camera_sensor(n_envs, show_viewer): scene = gs.Scene( - profiling_options=gs.options.ProfilingOptions(show_FPS=False), + profiling_options=gs.options.ProfilingOptions( + show_FPS=False, + ), show_viewer=show_viewer, ) - plane = scene.add_entity( + scene.add_entity( morph=gs.morphs.Plane(), - surface=gs.surfaces.Rough(color=(0.4, 0.4, 0.4)), + surface=gs.surfaces.Rough( + color=(0.4, 0.4, 0.4), + ), ) sphere = scene.add_entity( - morph=gs.morphs.Sphere(pos=(0.0, 0.0, 2.0), radius=0.5), - surface=gs.surfaces.Smooth(color=(1.0, 0.5, 0.5)), + morph=gs.morphs.Sphere( + radius=0.5, + pos=(0.0, 0.0, 2.0), + ), + surface=gs.surfaces.Smooth( + color=(1.0, 0.5, 0.5), + ), ) - box = scene.add_entity( - morph=gs.morphs.Box(pos=(1.0, 1.0, 1.0), size=(0.3, 0.3, 0.3)), - surface=gs.surfaces.Rough(color=(0.5, 1.0, 0.5)), + scene.add_entity( + morph=gs.morphs.Box( + size=(0.3, 0.3, 0.3), + pos=(1.0, 1.0, 1.0), + ), + surface=gs.surfaces.Rough( + color=(0.5, 1.0, 0.5), + ), ) raster_cam0 = scene.add_sensor( @@ -86,24 +100,22 @@ def test_rasterizer_camera_sensor(show_viewer, tol, n_envs): ) scene.build(n_envs=n_envs) - for i in range(10): + for _ in range(10): scene.step() data_cam0 = raster_cam0.read() data_cam1 = raster_cam1.read() data_attached = raster_cam_attached.read() data_offset_T = raster_cam_offset_T.read() - for cam_name, data in [ + for _cam_name, data in [ ("cam0", data_cam0), ("cam1", data_cam1), ("attached", data_attached), ("offset_T", data_offset_T), ]: - rgb = data.rgb - rgb_np = tensor_to_array(rgb) - mean_value = np.mean(rgb_np) - assert mean_value > 1.0 - assert mean_value < 254.0 + rgb_np = tensor_to_array(data.rgb) + mean = np.mean(rgb_np) + assert 1.0 < mean < 254.0 variance = np.var(rgb_np) assert variance > 1.0 data_env0 = raster_cam0.read(envs_idx=0) @@ -121,14 +133,14 @@ def _get_camera_world_pos(sensor): cam_pos_initial = _get_camera_world_pos(raster_cam_attached) cam_pos_initial_offset_T = _get_camera_world_pos(raster_cam_offset_T) - for i in range(10): # Test over multiple steps + for _ in range(10): # Test over multiple steps scene.step() - data_attached_moved = raster_cam_attached.read() + raster_cam_attached.read() cam_pos_final = _get_camera_world_pos(raster_cam_attached) cam_move_dist = np.linalg.norm(cam_pos_final - cam_pos_initial) assert cam_move_dist > 1e-2 - data_offset_T_moved = raster_cam_offset_T.read() + raster_cam_offset_T.read() cam_pos_final_offset_T = _get_camera_world_pos(raster_cam_offset_T) cam_move_dist_offset_T = np.linalg.norm(cam_pos_final_offset_T - cam_pos_initial_offset_T) assert cam_move_dist_offset_T > 1e-2 @@ -139,15 +151,9 @@ def _get_camera_world_pos(sensor): @pytest.mark.required +@pytest.mark.skipif(sys.platform == "darwin", reason="Not supported on this machine because it requires OpenGL 4.2.") def test_rasterizer_camera_sensor_n_envs(show_viewer, png_snapshot): - if sys.platform == "darwin": - pytest.skip( - "Batched rendering with Rasterizer backend is not supported on this machine because it requires OpenGL 4.2." - ) - scene = gs.Scene( - rigid_options=gs.options.RigidOptions(), - renderer=gs.renderers.Rasterizer(), show_viewer=show_viewer, ) @@ -162,16 +168,17 @@ def test_rasterizer_camera_sensor_n_envs(show_viewer, png_snapshot): morph=gs.morphs.Sphere(pos=(0.0, 0.0, 1.0), radius=0.3), surface=gs.surfaces.Smooth(color=(1.0, 0.5, 0.5)), ) - - options = gs.sensors.RasterizerCameraOptions( - res=(64, 64), pos=(3.0, 0.0, 1.5), lookat=(0.0, 0.0, 0.5), fov=60.0, draw_debug=show_viewer + camera = scene.add_sensor( + gs.sensors.RasterizerCameraOptions( + res=(64, 64), pos=(3.0, 0.0, 1.5), lookat=(0.0, 0.0, 0.5), fov=60.0, draw_debug=show_viewer + ) ) - camera = scene.add_sensor(options) - n_envs = 2 + scene.build(n_envs=2) - scene.build(n_envs=n_envs) - sphere.set_pos([[0.0, 0.0, 1.0], [0.2, 0.0, 0.5]]) + # Disable shadows systematically for Rasterizer because they are forcibly disabled on CPU backend anyway + camera._shared_metadata.context.shadow = False + sphere.set_pos([[0.0, 0.0, 1.0], [0.2, 0.0, 0.5]]) scene.step() data = camera.read() @@ -180,39 +187,30 @@ def test_rasterizer_camera_sensor_n_envs(show_viewer, png_snapshot): assert data.rgb.dtype == torch.uint8 assert (data.rgb[0] != data.rgb[1]).any(), "We should have different frames" - for i in range(n_envs): + for i in range(scene.n_envs): assert rgb_array_to_png_bytes(data.rgb[i]) == png_snapshot @pytest.mark.required -def test_rasterizer_camera_sensor_n_envs_attached_camera(show_viewer): - if sys.platform == "darwin": - pytest.skip( - "Batched rendering with Rasterizer backend is not supported on this machine because it requires OpenGL 4.2." - ) - - scene = gs.Scene( - rigid_options=gs.options.RigidOptions(), - renderer=gs.renderers.Rasterizer(), - show_viewer=show_viewer, - ) +@pytest.mark.skipif(sys.platform == "darwin", reason="Not supported on this machine because it requires OpenGL 4.2.") +def test_rasterizer_camera_sensor_n_envs_attached_camera(): + scene = gs.Scene() - # Add a sphere sphere = scene.add_entity( - morph=gs.morphs.Sphere(pos=(0.0, 0.0, 1.0), radius=0.3), - surface=gs.surfaces.Smooth(color=(1.0, 0.5, 0.5)), + morph=gs.morphs.Sphere( + radius=0.3, + pos=(0.0, 0.0, 1.0), + ), + surface=gs.surfaces.Smooth( + color=(1.0, 0.5, 0.5), + ), ) - options = gs.sensors.RasterizerCameraOptions( - res=(64, 64), - pos=(3.0, 0.0, 1.5), - lookat=(0.0, 0.0, 0.5), - fov=60.0, - entity_idx=sphere.idx, - draw_debug=show_viewer, + scene.add_sensor( + gs.sensors.RasterizerCameraOptions( + entity_idx=sphere.idx, + ) ) - scene.add_sensor(options) - n_envs = 2 with pytest.raises(gs.GenesisException, match="does not work with attached cameras yet."): - scene.build(n_envs=n_envs) + scene.build(n_envs=2) diff --git a/tests/utils.py b/tests/utils.py index a483c5a7f8..e6f1226928 100644 --- a/tests/utils.py +++ b/tests/utils.py @@ -36,7 +36,7 @@ DEFAULT_BRANCH_NAME = "main" HUGGINGFACE_ASSETS_REVISION = "701f78c1465f0a98f6540bae6c9daacaa551b7bf" -HUGGINGFACE_SNAPSHOT_REVISION = "1029f0b5ec874b93dc5170179f7f5a3e770b10ea" +HUGGINGFACE_SNAPSHOT_REVISION = "1df3aa3732abcf9b1701e180e2d266ac9d8d411e" MESH_EXTENSIONS = (".mtl", *MESH_FORMATS, *GLTF_FORMATS, *USD_FORMATS) IMAGE_EXTENSIONS = (".png", ".jpg")