@@ -239,3 +239,167 @@ def wrapped_step(dt):
239239 else :
240240 # If env creation failed, still clear the singleton
241241 SimulationContext .clear_instance ()
242+
243+
244+ @pytest .mark .parametrize ("env_type" , ["manager_based_env" , "manager_based_rl_env" , "direct_rl_env" ])
245+ def test_env_render_false_skips_rendering (env_type , physics_callback , render_callback ):
246+ """Test that setting render_enabled=False skips all rendering while physics continues."""
247+ physics_cb , get_physics_stats = physics_callback
248+ render_cb , get_render_stats = render_callback
249+
250+ env = None
251+ physics_handle = None
252+ original_step = None
253+ viz = None
254+
255+ try :
256+ # create a new stage
257+ sim_utils .create_new_stage ()
258+
259+ # create environment with render_interval=1 so rendering would happen every physics step
260+ if env_type == "manager_based_env" :
261+ env = create_manager_based_env (render_interval = 1 )
262+ elif env_type == "manager_based_rl_env" :
263+ env = create_manager_based_rl_env (render_interval = 1 )
264+ else :
265+ env = create_direct_rl_env (render_interval = 1 )
266+
267+ # enable the flag to render the environment
268+ env .sim .set_setting ("/isaaclab/render/rtx_sensors" , True )
269+
270+ # disable the app from shutting down when the environment is closed
271+ env .sim ._app_control_on_stop_handle = None # type: ignore
272+
273+ # Reset to initialize visualizers
274+ env .reset ()
275+
276+ # Ensure the default Kit visualizer is active for rendering callbacks.
277+ assert isinstance (env .sim .visualizers [0 ], KitVisualizer )
278+
279+ # add physics callback
280+ physics_handle = env .sim .physics_manager .register_callback (
281+ physics_cb , IsaacEvents .POST_PHYSICS_STEP , name = "physics_step"
282+ )
283+
284+ # Wrap visualizer step to track render calls
285+ viz = env .sim .visualizers [0 ]
286+ original_step = viz .step
287+ render_dt = env .cfg .sim .dt * env .cfg .sim .render_interval
288+
289+ def wrapped_step (dt ):
290+ original_step (dt )
291+ render_cb (render_dt )
292+
293+ viz .step = wrapped_step
294+
295+ # create a zero action tensor for stepping the environment
296+ actions = torch .zeros ((env .num_envs , 0 ), device = env .device )
297+
298+ # Step with render_enabled=False for several steps
299+ env .render_enabled = False
300+ for i in range (10 ):
301+ env .step (action = actions )
302+
303+ # Physics should still advance normally
304+ _ , num_physics_steps = get_physics_stats ()
305+ assert num_physics_steps == (i + 1 ) * env .cfg .decimation , "Physics steps mismatch with render_enabled=False"
306+
307+ # No rendering should have occurred
308+ _ , num_render_steps = get_render_stats ()
309+ assert num_render_steps == 0 , f"Expected 0 render steps with render_enabled=False, got { num_render_steps } "
310+
311+ finally :
312+ if viz is not None and original_step is not None :
313+ viz .step = original_step
314+ if physics_handle is not None :
315+ physics_handle .deregister ()
316+ if env is not None :
317+ env .close ()
318+ else :
319+ SimulationContext .clear_instance ()
320+
321+
322+ @pytest .mark .parametrize ("env_type" , ["manager_based_env" , "manager_based_rl_env" , "direct_rl_env" ])
323+ def test_env_render_flag_mixed_steps (env_type , physics_callback , render_callback ):
324+ """Test that render_enabled can be toggled between steps and rendering counts are correct."""
325+ physics_cb , get_physics_stats = physics_callback
326+ render_cb , get_render_stats = render_callback
327+
328+ env = None
329+ physics_handle = None
330+ original_step = None
331+ viz = None
332+
333+ try :
334+ # create a new stage
335+ sim_utils .create_new_stage ()
336+
337+ # create environment with render_interval=1 so every decimation step renders
338+ if env_type == "manager_based_env" :
339+ env = create_manager_based_env (render_interval = 1 )
340+ elif env_type == "manager_based_rl_env" :
341+ env = create_manager_based_rl_env (render_interval = 1 )
342+ else :
343+ env = create_direct_rl_env (render_interval = 1 )
344+
345+ # enable the flag to render the environment
346+ env .sim .set_setting ("/isaaclab/render/rtx_sensors" , True )
347+
348+ # disable the app from shutting down when the environment is closed
349+ env .sim ._app_control_on_stop_handle = None # type: ignore
350+
351+ # Reset to initialize visualizers
352+ env .reset ()
353+
354+ # Ensure the default Kit visualizer is active for rendering callbacks.
355+ assert isinstance (env .sim .visualizers [0 ], KitVisualizer )
356+
357+ # add physics callback
358+ physics_handle = env .sim .physics_manager .register_callback (
359+ physics_cb , IsaacEvents .POST_PHYSICS_STEP , name = "physics_step"
360+ )
361+
362+ # Wrap visualizer step to track render calls
363+ viz = env .sim .visualizers [0 ]
364+ original_step = viz .step
365+ render_dt = env .cfg .sim .dt * env .cfg .sim .render_interval
366+
367+ def wrapped_step (dt ):
368+ original_step (dt )
369+ render_cb (render_dt )
370+
371+ viz .step = wrapped_step
372+
373+ # create a zero action tensor for stepping the environment
374+ actions = torch .zeros ((env .num_envs , 0 ), device = env .device )
375+
376+ expected_render_steps = 0
377+
378+ # Step 5 times with render_enabled=True, then 5 with render_enabled=False
379+ for i in range (10 ):
380+ should_render = i < 5
381+ env .render_enabled = should_render
382+ env .step (action = actions )
383+
384+ # Physics always advances
385+ _ , num_physics_steps = get_physics_stats ()
386+ assert num_physics_steps == (i + 1 ) * env .cfg .decimation , "Physics steps mismatch in mixed test"
387+
388+ # Rendering only happens in the first 5 steps
389+ if should_render :
390+ expected_render_steps += env .cfg .decimation # render_interval=1, so renders every decimation step
391+
392+ _ , num_render_steps = get_render_stats ()
393+ assert num_render_steps == expected_render_steps , (
394+ f"Render steps mismatch at step { i } : expected { expected_render_steps } , got { num_render_steps } "
395+ )
396+
397+ finally :
398+ if viz is not None and original_step is not None :
399+ viz .step = original_step
400+ if physics_handle is not None :
401+ physics_handle .deregister ()
402+ if env is not None :
403+ env .close ()
404+ else :
405+ SimulationContext .clear_instance ()
0 commit comments