Skip to content

Commit 583b3ba

Browse files
committed
Save latest auto rendered frame as cache (Farama-Foundation#1010)
1 parent 18daeec commit 583b3ba

File tree

4 files changed

+32
-12
lines changed

4 files changed

+32
-12
lines changed

gymnasium/wrappers/rendering.py

Lines changed: 7 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -477,6 +477,7 @@ def __init__(self, env: gym.Env[ObsType, ActType], auto_rendering: bool = True):
477477
self.window = None # Has to be initialized before asserts, as self.window is used in auto close
478478
self.clock = None
479479
self.auto_rendering = auto_rendering
480+
self._latest_frame = None
480481

481482
assert (
482483
self.env.render_mode in self.ACCEPTED_RENDER_MODES
@@ -498,7 +499,7 @@ def step(self, action: ActType) -> tuple[ObsType, SupportsFloat, bool, bool, dic
498499
"""Perform a step in the base environment and render a frame to the screen."""
499500
result = super().step(action)
500501
if self.auto_rendering:
501-
self._render_frame()
502+
self._latest_frame = self._render_frame()
502503
return result
503504

504505
def reset(
@@ -507,11 +508,15 @@ def reset(
507508
"""Reset the base environment and render a frame to the screen."""
508509
result = super().reset(seed=seed, options=options)
509510
if self.auto_rendering:
510-
self._render_frame()
511+
self._latest_frame = self._render_frame()
511512
return result
512513

513514
def render(self):
514515
"""This method doesn't do much, actual rendering is usually performed in :meth:`step` and :meth:`reset`."""
516+
if self.auto_rendering and self._latest_frame is not None:
517+
frame = self._latest_frame
518+
self._latest_frame = None
519+
return frame
515520
return self._render_frame()
516521

517522
def _render_frame(self):

gymnasium/wrappers/vector/rendering.py

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -65,7 +65,7 @@ def step(
6565
"""Perform a step in the base environment and render a frame to the screen."""
6666
result = super().step(actions)
6767
if self.auto_rendering:
68-
self._render_frame()
68+
self._latest_frame = self._render_frame()
6969
return result
7070

7171
def reset(
@@ -77,11 +77,15 @@ def reset(
7777
"""Reset the base environment and render a frame to the screen."""
7878
result = super().reset(seed=seed, options=options)
7979
if self.auto_rendering:
80-
self._render_frame()
80+
self._latest_frame = self._render_frame()
8181
return result
8282

8383
def render(self):
8484
"""This method doesn't do much, actual rendering is usually performed in :meth:`step` and :meth:`reset`."""
85+
if self.auto_rendering and self._latest_frame is not None:
86+
frame = self._latest_frame
87+
self._latest_frame = None
88+
return frame
8589
return self._render_frame()
8690

8791
def _render_frame(self):

tests/wrappers/test_human_rendering.py

Lines changed: 6 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -37,20 +37,23 @@ def test_human_rendering():
3737
@pytest.mark.parametrize("env_id", ["CartPole-v1"])
3838
@pytest.mark.parametrize("num_envs", [1, 3, 9])
3939
@pytest.mark.parametrize("screen_size", [None])
40-
def test_human_rendering_manual(env_id, num_envs, screen_size):
40+
@pytest.mark.parametrize("auto_rendering", [False, True])
41+
def test_human_rendering_manual(env_id, num_envs, screen_size, auto_rendering):
4142
env = HumanRendering(
4243
gym.make("CartPole-v1", render_mode="rgb_array", disable_env_checker=True),
43-
auto_rendering=False,
44+
auto_rendering=auto_rendering,
4445
)
4546
assert env.render_mode == "human"
47+
assert env.auto_rendering == auto_rendering
48+
4649
env.reset()
4750

4851
for _ in range(75):
4952
_, _, terminated, truncated, _ = env.step(env.action_space.sample())
5053
if terminated or truncated:
5154
env.reset()
52-
rendering = env.render()
5355
# output should match mode
56+
rendering = env.render()
5457
assert isinstance(rendering, np.ndarray)
5558

5659
env.close()

tests/wrappers/vector/test_human_rendering.py

Lines changed: 13 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -25,14 +25,21 @@ def test_num_envs_screen_size(env_id, num_envs, screen_size):
2525

2626

2727
def test_render_modes():
28+
num_envs = 3
2829
envs = HumanRendering(
29-
gym.make_vec("CartPole-v1", num_envs=3, render_mode="rgb_array_list")
30+
gym.make_vec("CartPole-v1", num_envs=num_envs, render_mode="rgb_array_list")
3031
)
3132
assert envs.render_mode == "human"
3233

3334
envs.reset()
3435
for _ in range(25):
3536
envs.step(envs.action_space.sample())
37+
# output should match mode, list of environment rgb_arrays
38+
rendering = envs.render()
39+
assert isinstance(rendering, list)
40+
assert len(rendering) == num_envs
41+
assert isinstance(rendering[0], np.ndarray)
42+
3643
envs.close()
3744

3845
# HumanRenderer on human renderer should not work
@@ -48,19 +55,20 @@ def test_render_modes():
4855
@pytest.mark.parametrize("env_id", ["CartPole-v1"])
4956
@pytest.mark.parametrize("num_envs", [1, 3, 9])
5057
@pytest.mark.parametrize("screen_size", [None])
51-
def test_human_rendering_manual(env_id, num_envs, screen_size):
58+
@pytest.mark.parametrize("auto_rendering", [False, True])
59+
def test_human_rendering_manual(env_id, num_envs, screen_size, auto_rendering):
5260
envs = gym.make_vec(env_id, num_envs=num_envs, render_mode="rgb_array")
53-
envs = HumanRendering(envs, screen_size=screen_size, auto_rendering=False)
61+
envs = HumanRendering(envs, screen_size=screen_size, auto_rendering=auto_rendering)
5462

5563
assert envs.render_mode == "human"
56-
assert not envs.auto_rendering
64+
assert envs.auto_rendering == auto_rendering
5765

5866
envs.reset()
5967

6068
# Test Manual render() call
6169
envs.step(envs.action_space.sample())
62-
rendering = envs.render()
6370
# output should match mode, list of environment rgb_arrays
71+
rendering = envs.render()
6472
assert isinstance(rendering, list)
6573
assert len(rendering) == num_envs
6674
assert isinstance(rendering[0], np.ndarray)

0 commit comments

Comments
 (0)