From e72e16abb02a930a42f25fa5fbd61cf5570e7f6b Mon Sep 17 00:00:00 2001 From: mpragnay Date: Sat, 31 Jan 2026 20:26:58 -0500 Subject: [PATCH 1/7] Added render_eval mode, minor bug fixes for building in clang --- pufferlib/config/ocean/drive.ini | 12 +++++-- pufferlib/ocean/drive/drive.h | 1 + pufferlib/pufferl.py | 61 ++++++++++++++++++++++++++++++++ 3 files changed, 71 insertions(+), 3 deletions(-) diff --git a/pufferlib/config/ocean/drive.ini b/pufferlib/config/ocean/drive.ini index e8a650864..bc23bbbcb 100644 --- a/pufferlib/config/ocean/drive.ini +++ b/pufferlib/config/ocean/drive.ini @@ -5,8 +5,8 @@ policy_name = Drive rnn_name = Recurrent [vec] -num_workers = 16 -num_envs = 16 +num_workers = 8 +num_envs = 8 batch_size = 4 ; backend = Serial @@ -47,7 +47,7 @@ episode_length = 91 resample_frequency = 910 termination_mode = 1 # 0 - terminate at episode_length, 1 - terminate after all agents have been reset map_dir = "resources/drive/binaries/training" -num_maps = 10000 +num_maps = 1000 ; If True, allows training with fewer maps than requested (warns instead of erroring) allow_fewer_maps = True ; Determines which step of the trajectory to initialize the agents at upon reset @@ -130,6 +130,12 @@ wosac_aggregate_results = True human_replay_eval = False ; Control only the self-driving car human_replay_control_mode = "control_sdc_only" +; Renders first num_maps videos from map_dir directory +render_videos_eval = True +; "both", "topdown", "agent"; Other args are passed from train confs +render_view_mode = "both" +; Policy bin file used for rendering videos +render_policy_path = "resources/drive/puffer_drive_weights_resampling_300.bin" [sweep.train.learning_rate] distribution = log_normal diff --git a/pufferlib/ocean/drive/drive.h b/pufferlib/ocean/drive/drive.h index e53c88b8d..94248cb15 100644 --- a/pufferlib/ocean/drive/drive.h +++ b/pufferlib/ocean/drive/drive.h @@ -332,6 +332,7 @@ float point_to_segment_distance_2d(float px, float py, float x1, float y1, float void init_goal_positions(Drive *env); float clipSpeed(float speed); void sample_new_goal(Drive *env, int agent_idx); +int check_lane_aligned(Entity *car, Entity *lane, int geometry_idx); // ======================================== // Utility Functions diff --git a/pufferlib/pufferl.py b/pufferlib/pufferl.py index a8a131583..24c4f8eec 100644 --- a/pufferlib/pufferl.py +++ b/pufferlib/pufferl.py @@ -24,6 +24,7 @@ import numpy as np import psutil +from multiprocessing.pool import ThreadPool import torch import torch.distributed @@ -1040,6 +1041,7 @@ def eval(env_name, args=None, vecenv=None, policy=None): wosac_enabled = args["eval"]["wosac_realism_eval"] human_replay_enabled = args["eval"]["human_replay_eval"] + render_videos_enabled = args["eval"]["render_videos_eval"] args["env"]["map_dir"] = args["eval"]["map_dir"] args["env"]["num_maps"] = args["eval"]["num_maps"] args["env"]["use_all_maps"] = True @@ -1122,6 +1124,65 @@ def eval(env_name, args=None, vecenv=None, policy=None): print("HUMAN_REPLAY_METRICS_END") return results + + elif render_videos_enabled: + # Renders first num_maps from map_dir using visualize binary + map_dir = args["eval"]["map_dir"] + num_maps = args["eval"]["num_maps"] + view_mode = args["eval"].get("render_view_mode", "both") + render_policy_path = args["eval"].get("render_policy_path", None) + num_workers = args["vec"]["num_workers"] + render_maps = [os.path.join(map_dir, f) for f in sorted(os.listdir(map_dir)) if f.endswith(".bin")][:num_maps] + output_dir = "resources/drive/render_videos" + os.makedirs(output_dir, exist_ok=True) + + # Rebuild visualize binary + ensure_drive_binary() + + def render_task(map_path): + base_cmd = ( + ["./visualize"] + if sys.platform == "darwin" + else ["xvfb-run", "-a", "-s", "-screen 0 1280x720x24", "./visualize"] + ) + cmd = base_cmd.copy() + cmd.extend(["--map-name", map_path]) + train_configs = args["train"] + if train_configs.get("show_grid", False): + cmd.append("--show-grid") + if train_configs.get("obs_only", False): + cmd.append("--obs-only") + if train_configs.get("show_lasers", False): + cmd.append("--lasers") + if train_configs.get("show_human_logs", False): + cmd.append("--show-human-logs") + if train_configs.get("zoom_in", False): + cmd.append("--zoom-in") + if train_configs.get("frame_skip", 1) > 1: + cmd.extend(["--frame-skip", str(train_configs["frame_skip"])]) + cmd.extend(["--view", view_mode]) + if render_policy_path is not None: + cmd.extend(["--policy-name", render_policy_path]) + + map_name = os.path.basename(map_path).replace(".bin", "") + + if view_mode == "topdown" or view_mode == "both": + cmd.extend(["--output-topdown", output_dir + f"/topdown_{map_name}.mp4"]) + if view_mode == "agent" or view_mode == "both": + cmd.extend(["--output-agent", output_dir + f"/agent_{map_name}.mp4"]) + + env_vars = os.environ.copy() + env_vars["ASAN_OPTIONS"] = "exitcode=0" + result = subprocess.run(cmd, cwd=os.getcwd(), capture_output=True, text=True, timeout=600, env=env_vars) + if result.returncode != 0: + print(f"Error rendering {map_name}: {result.stderr}") + + if render_maps: + print(f"Rendering {len(render_maps)} from {map_dir} with {num_workers} workers...") + with ThreadPool(num_workers) as pool: + pool.map(render_task, render_maps) + print(f"Finished rendering videos to {output_dir}") + else: # Standard evaluation: Render backend = args["vec"]["backend"] if backend != "PufferEnv": From 4067498eb8a51a438e1b29d0f23f21f13cf50474 Mon Sep 17 00:00:00 2001 From: mpragnay Date: Sat, 31 Jan 2026 20:53:16 -0500 Subject: [PATCH 2/7] revert to default configs --- pufferlib/config/ocean/drive.ini | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/pufferlib/config/ocean/drive.ini b/pufferlib/config/ocean/drive.ini index bc23bbbcb..fa5eccd6f 100644 --- a/pufferlib/config/ocean/drive.ini +++ b/pufferlib/config/ocean/drive.ini @@ -131,11 +131,11 @@ human_replay_eval = False ; Control only the self-driving car human_replay_control_mode = "control_sdc_only" ; Renders first num_maps videos from map_dir directory -render_videos_eval = True +render_videos_eval = False ; "both", "topdown", "agent"; Other args are passed from train confs render_view_mode = "both" ; Policy bin file used for rendering videos -render_policy_path = "resources/drive/puffer_drive_weights_resampling_300.bin" +render_policy_path = "resources/drive/puffer_drive_weights.bin" [sweep.train.learning_rate] distribution = log_normal From 26b7940d8499eed0444f568de75eddce58c61df4 Mon Sep 17 00:00:00 2001 From: mpragnay Date: Sat, 31 Jan 2026 20:55:57 -0500 Subject: [PATCH 3/7] minor changes --- pufferlib/config/ocean/drive.ini | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/pufferlib/config/ocean/drive.ini b/pufferlib/config/ocean/drive.ini index fa5eccd6f..a2808f32a 100644 --- a/pufferlib/config/ocean/drive.ini +++ b/pufferlib/config/ocean/drive.ini @@ -5,8 +5,8 @@ policy_name = Drive rnn_name = Recurrent [vec] -num_workers = 8 -num_envs = 8 +num_workers = 16 +num_envs = 16 batch_size = 4 ; backend = Serial @@ -47,7 +47,7 @@ episode_length = 91 resample_frequency = 910 termination_mode = 1 # 0 - terminate at episode_length, 1 - terminate after all agents have been reset map_dir = "resources/drive/binaries/training" -num_maps = 1000 +num_maps = 10000 ; If True, allows training with fewer maps than requested (warns instead of erroring) allow_fewer_maps = True ; Determines which step of the trajectory to initialize the agents at upon reset From 63d1cf1df3db23890226c114bafdd4e99879a733 Mon Sep 17 00:00:00 2001 From: mpragnay Date: Sat, 31 Jan 2026 21:08:18 -0500 Subject: [PATCH 4/7] Added Documentation for mode --- docs/src/visualizer.md | 11 +++++++++++ pufferlib/pufferl.py | 4 ++++ 2 files changed, 15 insertions(+) diff --git a/docs/src/visualizer.md b/docs/src/visualizer.md index c6b73c90e..2a6b247d5 100644 --- a/docs/src/visualizer.md +++ b/docs/src/visualizer.md @@ -33,3 +33,14 @@ xvfb-run -s "-screen 0 1280x720x24" ./visualize ``` Adjust the screen size and color depth as needed. The `xvfb-run` wrapper allows Raylib to render without an attached display, which is convenient for servers and CI jobs. + +## Evaluation Rendering +You can batch render videos for multiple maps using the evaluation mode. This will render the first `num_maps`(limited by number of maps in the directory) from `map_dir` in parallel using the `visualize` binary. + +To enable this, set `render_videos_eval = True` and configure `eval.num_maps` in your `drive.ini`. Then run: + +```bash +puffer eval puffer_drive +``` + +This mode parallelizes rendering based on `vec.num_workers`. Videos are saved to `resources/drive/render_videos`. diff --git a/pufferlib/pufferl.py b/pufferlib/pufferl.py index 24c4f8eec..e1270d5fd 100644 --- a/pufferlib/pufferl.py +++ b/pufferlib/pufferl.py @@ -1132,6 +1132,10 @@ def eval(env_name, args=None, vecenv=None, policy=None): view_mode = args["eval"].get("render_view_mode", "both") render_policy_path = args["eval"].get("render_policy_path", None) num_workers = args["vec"]["num_workers"] + + if num_maps > len(os.listdir(map_dir)): + num_maps = len(os.listdir(map_dir)) + render_maps = [os.path.join(map_dir, f) for f in sorted(os.listdir(map_dir)) if f.endswith(".bin")][:num_maps] output_dir = "resources/drive/render_videos" os.makedirs(output_dir, exist_ok=True) From 9dbc4c14350c2fdcacaf6df44b1b63bba3f2a95b Mon Sep 17 00:00:00 2001 From: mpragnay Date: Sun, 1 Feb 2026 14:55:11 -0500 Subject: [PATCH 5/7] String Concatenation fixes, exception handling for timeout, doc suggestions by greptile --- docs/src/visualizer.md | 2 +- pufferlib/pufferl.py | 13 ++++++++----- 2 files changed, 9 insertions(+), 6 deletions(-) diff --git a/docs/src/visualizer.md b/docs/src/visualizer.md index 2a6b247d5..d0d378421 100644 --- a/docs/src/visualizer.md +++ b/docs/src/visualizer.md @@ -35,7 +35,7 @@ xvfb-run -s "-screen 0 1280x720x24" ./visualize Adjust the screen size and color depth as needed. The `xvfb-run` wrapper allows Raylib to render without an attached display, which is convenient for servers and CI jobs. ## Evaluation Rendering -You can batch render videos for multiple maps using the evaluation mode. This will render the first `num_maps`(limited by number of maps in the directory) from `map_dir` in parallel using the `visualize` binary. +You can batch render videos for multiple maps using the evaluation mode. This will render the first `num_maps` maps (capped by the number of maps in the directory) from `map_dir` in parallel using the `visualize` binary. To enable this, set `render_videos_eval = True` and configure `eval.num_maps` in your `drive.ini`. Then run: diff --git a/pufferlib/pufferl.py b/pufferlib/pufferl.py index e1270d5fd..8866b2402 100644 --- a/pufferlib/pufferl.py +++ b/pufferlib/pufferl.py @@ -1171,15 +1171,18 @@ def render_task(map_path): map_name = os.path.basename(map_path).replace(".bin", "") if view_mode == "topdown" or view_mode == "both": - cmd.extend(["--output-topdown", output_dir + f"/topdown_{map_name}.mp4"]) + cmd.extend(["--output-topdown", os.path.join(output_dir, f"topdown_{map_name}.mp4")]) if view_mode == "agent" or view_mode == "both": - cmd.extend(["--output-agent", output_dir + f"/agent_{map_name}.mp4"]) + cmd.extend(["--output-agent", os.path.join(output_dir, f"agent_{map_name}.mp4")]) env_vars = os.environ.copy() env_vars["ASAN_OPTIONS"] = "exitcode=0" - result = subprocess.run(cmd, cwd=os.getcwd(), capture_output=True, text=True, timeout=600, env=env_vars) - if result.returncode != 0: - print(f"Error rendering {map_name}: {result.stderr}") + try: + result = subprocess.run(cmd, cwd=os.getcwd(), capture_output=True, text=True, timeout=600, env=env_vars) + if result.returncode != 0: + print(f"Error rendering {map_name}: {result.stderr}") + except subprocess.TimeoutExpired: + print(f"Timeout rendering {map_name}: exceeded 600 seconds") if render_maps: print(f"Rendering {len(render_maps)} from {map_dir} with {num_workers} workers...") From 8ba1e3cd2a9ac54c32d39a1158139466aa225eee Mon Sep 17 00:00:00 2001 From: mpragnay Date: Mon, 2 Feb 2026 14:40:02 -0500 Subject: [PATCH 6/7] Created a Separate Render Mode, updated docs --- docs/src/visualizer.md | 11 ++-- pufferlib/config/ocean/drive.ini | 27 ++++++++-- pufferlib/pufferl.py | 86 +++++++++++++++++++++++++++++++- 3 files changed, 113 insertions(+), 11 deletions(-) diff --git a/docs/src/visualizer.md b/docs/src/visualizer.md index d0d378421..b241f0134 100644 --- a/docs/src/visualizer.md +++ b/docs/src/visualizer.md @@ -34,13 +34,12 @@ xvfb-run -s "-screen 0 1280x720x24" ./visualize Adjust the screen size and color depth as needed. The `xvfb-run` wrapper allows Raylib to render without an attached display, which is convenient for servers and CI jobs. -## Evaluation Rendering -You can batch render videos for multiple maps using the evaluation mode. This will render the first `num_maps` maps (capped by the number of maps in the directory) from `map_dir` in parallel using the `visualize` binary. - -To enable this, set `render_videos_eval = True` and configure `eval.num_maps` in your `drive.ini`. Then run: +## Rendering Mode +You can batch render videos for multiple maps using the evaluation mode. This will render the first `num_maps` maps (capped by the number of maps in the directory) from `map_dir` in parallel using the `visualize` binary and create these videos in an `output_dir`(All configs in [render] of `drive.ini`). +After setting the configs run: ```bash -puffer eval puffer_drive +puffer render puffer_drive ``` -This mode parallelizes rendering based on `vec.num_workers`. Videos are saved to `resources/drive/render_videos`. +This mode parallelizes rendering based on `vec.num_workers`. diff --git a/pufferlib/config/ocean/drive.ini b/pufferlib/config/ocean/drive.ini index e53f19490..4d9e70231 100644 --- a/pufferlib/config/ocean/drive.ini +++ b/pufferlib/config/ocean/drive.ini @@ -131,12 +131,31 @@ wosac_aggregate_results = True human_replay_eval = False ; Control only the self-driving car human_replay_control_mode = "control_sdc_only" -; Renders first num_maps videos from map_dir directory -render_videos_eval = False + +[render] +; Mode to render a bunch of maps with a given policy +; Path to dataset used for rendering +map_dir = "resources/drive/binaries/training" +; Directory to output rendered videos +output_dir = "resources/drive/render_videos" +; Evaluation will run on the first num_maps maps in the map_dir directory +num_maps = 100 ; "both", "topdown", "agent"; Other args are passed from train confs -render_view_mode = "both" +view_mode = "both" ; Policy bin file used for rendering videos -render_policy_path = "resources/drive/puffer_drive_weights.bin" +policy_path = "resources/drive/puffer_drive_weights_resampling_300.bin" +; Allows more than cpu cores workers for rendering +overwork = True +; If True, show exactly what the agent sees in agent observation +obs_only = True +; Show grid lines +show_grid = True +; Draws lines from ego agent observed ORUs and road elements to show detection range +show_lasers = True +; Display human xy logs in the background +show_human_logs = False +; If True, zoom in on a part of the map. Otherwise, show full map +zoom_in = True [sweep.train.learning_rate] distribution = log_normal diff --git a/pufferlib/pufferl.py b/pufferlib/pufferl.py index 8d81dc234..27cffa396 100644 --- a/pufferlib/pufferl.py +++ b/pufferlib/pufferl.py @@ -1711,8 +1711,90 @@ def puffer_type(value): return args +def render(env_name, args=None): + args = args or load_config(env_name) + render_configs = args.get("render", {}) + + # Renders first num_maps from map_dir using visualize binary + try: + map_dir = render_configs["map_dir"] + num_maps = render_configs.get("num_maps", 1) + view_mode = render_configs["view_mode"] + render_policy_path = render_configs["policy_path"] + overwork = render_configs.get("overwork", False) + num_workers = args["vec"]["num_workers"] + output_dir = render_configs["output_dir"] + except KeyError as e: + raise pufferlib.APIUsageError(f"Missing render config: {e}") + + cpu_cores = psutil.cpu_count(logical=False) + if num_workers > cpu_cores and not overwork: + raise pufferlib.APIUsageError( + " ".join( + [ + f"num_workers ({num_workers}) > hardware cores ({cpu_cores}) is disallowed by default.", + "PufferLib multiprocessing is heavily optimized for 1 process per hardware core.", + "If you really want to do this, set overwork=True (--vec-overwork in our demo.py).", + ] + ) + ) + + if num_maps > len(os.listdir(map_dir)): + num_maps = len(os.listdir(map_dir)) + + render_maps = [os.path.join(map_dir, f) for f in sorted(os.listdir(map_dir)) if f.endswith(".bin")][:num_maps] + os.makedirs(output_dir, exist_ok=True) + + # Rebuild visualize binary + ensure_drive_binary() + + def render_task(map_path): + base_cmd = ( + ["./visualize"] + if sys.platform == "darwin" + else ["xvfb-run", "-a", "-s", "-screen 0 1280x720x24", "./visualize"] + ) + cmd = base_cmd.copy() + cmd.extend(["--map-name", map_path]) + if render_configs.get("show_grid", False): + cmd.append("--show-grid") + if render_configs.get("obs_only", False): + cmd.append("--obs-only") + if render_configs.get("show_lasers", False): + cmd.append("--lasers") + if render_configs.get("show_human_logs", False): + cmd.append("--show-human-logs") + if render_configs.get("zoom_in", False): + cmd.append("--zoom-in") + cmd.extend(["--view", view_mode]) + if render_policy_path is not None: + cmd.extend(["--policy-name", render_policy_path]) + + map_name = os.path.basename(map_path).replace(".bin", "") + + if view_mode == "topdown" or view_mode == "both": + cmd.extend(["--output-topdown", os.path.join(output_dir, f"topdown_{map_name}.mp4")]) + if view_mode == "agent" or view_mode == "both": + cmd.extend(["--output-agent", os.path.join(output_dir, f"agent_{map_name}.mp4")]) + + env_vars = os.environ.copy() + env_vars["ASAN_OPTIONS"] = "exitcode=0" + try: + result = subprocess.run(cmd, cwd=os.getcwd(), capture_output=True, text=True, timeout=600, env=env_vars) + if result.returncode != 0: + print(f"Error rendering {map_name}: {result.stderr}") + except subprocess.TimeoutExpired: + print(f"Timeout rendering {map_name}: exceeded 600 seconds") + + if render_maps: + print(f"Rendering {len(render_maps)} from {map_dir} with {num_workers} workers...") + with ThreadPool(num_workers) as pool: + pool.map(render_task, render_maps) + print(f"Finished rendering videos to {output_dir}") + + def main(): - err = "Usage: puffer [train, eval, sweep, controlled_exp, autotune, profile, export, sanity] [env_name] [optional args]. --help for more info" + err = "Usage: puffer [train, eval, sweep, controlled_exp, autotune, profile, export, sanity, render] [env_name] [optional args]. --help for more info" if len(sys.argv) < 3: raise pufferlib.APIUsageError(err) @@ -1734,6 +1816,8 @@ def main(): export(env_name=env_name) elif mode == "sanity": sanity(env_name=env_name) + elif mode == "render": + render(env_name=env_name) else: raise pufferlib.APIUsageError(err) From 4372e4490143053a0ed5c6504b576e0cfb756331 Mon Sep 17 00:00:00 2001 From: mpragnay Date: Mon, 2 Feb 2026 14:49:53 -0500 Subject: [PATCH 7/7] code cleanup --- pufferlib/pufferl.py | 70 -------------------------------------------- 1 file changed, 70 deletions(-) diff --git a/pufferlib/pufferl.py b/pufferlib/pufferl.py index 27cffa396..1c80de553 100644 --- a/pufferlib/pufferl.py +++ b/pufferlib/pufferl.py @@ -1151,7 +1151,6 @@ def eval(env_name, args=None, vecenv=None, policy=None): wosac_enabled = args["eval"]["wosac_realism_eval"] human_replay_enabled = args["eval"]["human_replay_eval"] - render_videos_enabled = args["eval"]["render_videos_eval"] args["env"]["map_dir"] = args["eval"]["map_dir"] args["env"]["num_maps"] = args["eval"]["num_maps"] args["env"]["use_all_maps"] = True @@ -1235,71 +1234,6 @@ def eval(env_name, args=None, vecenv=None, policy=None): return results - elif render_videos_enabled: - # Renders first num_maps from map_dir using visualize binary - map_dir = args["eval"]["map_dir"] - num_maps = args["eval"]["num_maps"] - view_mode = args["eval"].get("render_view_mode", "both") - render_policy_path = args["eval"].get("render_policy_path", None) - num_workers = args["vec"]["num_workers"] - - if num_maps > len(os.listdir(map_dir)): - num_maps = len(os.listdir(map_dir)) - - render_maps = [os.path.join(map_dir, f) for f in sorted(os.listdir(map_dir)) if f.endswith(".bin")][:num_maps] - output_dir = "resources/drive/render_videos" - os.makedirs(output_dir, exist_ok=True) - - # Rebuild visualize binary - ensure_drive_binary() - - def render_task(map_path): - base_cmd = ( - ["./visualize"] - if sys.platform == "darwin" - else ["xvfb-run", "-a", "-s", "-screen 0 1280x720x24", "./visualize"] - ) - cmd = base_cmd.copy() - cmd.extend(["--map-name", map_path]) - train_configs = args["train"] - if train_configs.get("show_grid", False): - cmd.append("--show-grid") - if train_configs.get("obs_only", False): - cmd.append("--obs-only") - if train_configs.get("show_lasers", False): - cmd.append("--lasers") - if train_configs.get("show_human_logs", False): - cmd.append("--show-human-logs") - if train_configs.get("zoom_in", False): - cmd.append("--zoom-in") - if train_configs.get("frame_skip", 1) > 1: - cmd.extend(["--frame-skip", str(train_configs["frame_skip"])]) - cmd.extend(["--view", view_mode]) - if render_policy_path is not None: - cmd.extend(["--policy-name", render_policy_path]) - - map_name = os.path.basename(map_path).replace(".bin", "") - - if view_mode == "topdown" or view_mode == "both": - cmd.extend(["--output-topdown", os.path.join(output_dir, f"topdown_{map_name}.mp4")]) - if view_mode == "agent" or view_mode == "both": - cmd.extend(["--output-agent", os.path.join(output_dir, f"agent_{map_name}.mp4")]) - - env_vars = os.environ.copy() - env_vars["ASAN_OPTIONS"] = "exitcode=0" - try: - result = subprocess.run(cmd, cwd=os.getcwd(), capture_output=True, text=True, timeout=600, env=env_vars) - if result.returncode != 0: - print(f"Error rendering {map_name}: {result.stderr}") - except subprocess.TimeoutExpired: - print(f"Timeout rendering {map_name}: exceeded 600 seconds") - - if render_maps: - print(f"Rendering {len(render_maps)} from {map_dir} with {num_workers} workers...") - with ThreadPool(num_workers) as pool: - pool.map(render_task, render_maps) - print(f"Finished rendering videos to {output_dir}") - else: # Standard evaluation: Render backend = args["vec"]["backend"] if backend != "PufferEnv": @@ -1314,10 +1248,6 @@ def render_task(map_path): num_agents = vecenv.observation_space.shape[0] device = args["train"]["device"] - # Rebuild visualize binary if saving frames (for C-based rendering) - if args["save_frames"] > 0: - ensure_drive_binary() - state = {} if args["train"]["use_rnn"]: state = dict(