From f0a402d42012291cddbae4a3945ca51f0712430f Mon Sep 17 00:00:00 2001 From: royjr Date: Sun, 23 Aug 2026 20:15:54 -0400 Subject: [PATCH] egpu: show progress --- openpilot/common/params_keys.h | 1 + openpilot/selfdrive/modeld/load_progress.py | 45 +++++++++++++++++++ openpilot/selfdrive/modeld/modeld.py | 4 +- .../selfdrive/ui/mici/onroad/hud_renderer.py | 19 +++++++- openpilot/selfdrive/ui/ui_state.py | 4 ++ openpilot/sunnypilot/modeld_v2/modeld.py | 4 +- 6 files changed, 74 insertions(+), 3 deletions(-) create mode 100644 openpilot/selfdrive/modeld/load_progress.py diff --git a/openpilot/common/params_keys.h b/openpilot/common/params_keys.h index 01c14fb539..2be75a050e 100644 --- a/openpilot/common/params_keys.h +++ b/openpilot/common/params_keys.h @@ -132,6 +132,7 @@ inline static std::unordered_map keys = { {"UptimeOnroad", {PERSISTENT, FLOAT, "0.0"}}, {"UsbGpuActive", {CLEAR_ON_MANAGER_START | CLEAR_ON_OFFROAD_TRANSITION | CLEAR_ON_IGNITION_ON, BOOL}}, {"UsbGpuLoading", {CLEAR_ON_MANAGER_START | CLEAR_ON_OFFROAD_TRANSITION | CLEAR_ON_IGNITION_ON, BOOL}}, + {"UsbGpuLoadProgress", {CLEAR_ON_MANAGER_START | CLEAR_ON_OFFROAD_TRANSITION | CLEAR_ON_IGNITION_ON, INT, "0"}}, {"Version", {PERSISTENT, STRING}}, // --- sunnypilot params --- // diff --git a/openpilot/selfdrive/modeld/load_progress.py b/openpilot/selfdrive/modeld/load_progress.py new file mode 100644 index 0000000000..536e584432 --- /dev/null +++ b/openpilot/selfdrive/modeld/load_progress.py @@ -0,0 +1,45 @@ +import os +from openpilot.common.file_chunker import open_file_chunked, get_existing_chunks +from openpilot.common.params import Params + +PARAM = "UsbGpuLoadProgress" + + +class ProgressReader: + def __init__(self, inner, total): + self._inner = inner + self._total = total + self._params = Params() + self._read = 0 + self._pct = -1 + self._step = max(64 * 1024, total // 100) + + def _bump(self, n): + self._read += n + if self._total: + pct = min(100, self._read * 100 // self._total) + if pct != self._pct: + self._pct = pct + self._params.put(PARAM, pct) + + def read(self, size=-1): + data = self._inner.read(size) + self._bump(len(data)) + return data + + def readinto(self, b): + view = memoryview(b) + done = 0 + while done < len(view): + n = self._inner.readinto(view[done:done + self._step]) + if not n: + break + done += n + self._bump(n) + return done + + +def open_with_progress(pkl_path): + chunks = [p for p in get_existing_chunks(pkl_path) if not p.endswith(".chunkmanifest")] + total = sum(os.path.getsize(p) for p in chunks) + return ProgressReader(open_file_chunked(pkl_path), total) diff --git a/openpilot/selfdrive/modeld/modeld.py b/openpilot/selfdrive/modeld/modeld.py index 790ae7b167..62e4867efb 100755 --- a/openpilot/selfdrive/modeld/modeld.py +++ b/openpilot/selfdrive/modeld/modeld.py @@ -29,6 +29,7 @@ from openpilot.selfdrive.modeld.parse_model_outputs import Parser from openpilot.selfdrive.modeld.compile_modeld import make_input_queues, WARP_INPUTS, POLICY_INPUTS from openpilot.selfdrive.modeld.fill_model_msg import fill_model_msg, fill_driving_model_data, fill_pose_msg, PublishState from openpilot.common.file_chunker import open_file_chunked +from openpilot.selfdrive.modeld.load_progress import open_with_progress from openpilot.selfdrive.modeld.constants import ModelConstants, Plan from openpilot.selfdrive.modeld.helpers import usbgpu_present, usbgpu_compiled, modeld_pkl_path, get_tg_input_devices, load_oob @@ -145,7 +146,7 @@ class ModelState(ModelStateBase): ModelStateBase.__init__(self) input_devices = get_tg_input_devices(PROCESS_NAME, usbgpu) self.WARP_DEV, self.QUEUE_DEV = input_devices['WARP_DEV'], input_devices['QUEUE_DEV'] - jits = load_oob(open_file_chunked(modeld_pkl_path(usbgpu))) + jits = load_oob(open_with_progress(modeld_pkl_path(usbgpu)) if usbgpu else open_file_chunked(modeld_pkl_path(usbgpu))) metadata = jits['metadata'] self.input_shapes = metadata['input_shapes'] self.vision_input_names = [k for k in self.input_shapes if 'img' in k] @@ -223,6 +224,7 @@ def main(demo=False): os.environ['HCQDEV_WAIT_TIMEOUT_MS'] = '3000' params = Params() params.put_bool("UsbGpuLoading", USBGPU) + params.put("UsbGpuLoadProgress", 0) params.remove("UsbGpuActive") config_realtime_process(7, 54) diff --git a/openpilot/selfdrive/ui/mici/onroad/hud_renderer.py b/openpilot/selfdrive/ui/mici/onroad/hud_renderer.py index 2d1f954122..f45c3e0b26 100644 --- a/openpilot/selfdrive/ui/mici/onroad/hud_renderer.py +++ b/openpilot/selfdrive/ui/mici/onroad/hud_renderer.py @@ -223,7 +223,7 @@ class HudRenderer(Widget): if icon is not self._egpu_icon: self._egpu_fade_time = rl.get_time() self._egpu_icon = icon - alpha = self._egpu_alpha_filter.update(loading or 0 < rl.get_time() - self._egpu_fade_time < SET_SPEED_PERSISTENCE) + alpha = self._egpu_alpha_filter.update(True) if alpha < 1e-2: return @@ -231,6 +231,23 @@ class HudRenderer(Widget): rect.y + rect.height - 14 - (self._txt_wheel.height + icon.height) / 2) rl.draw_texture_ex(icon, pos, 0.0, 1.0, rl.Color(255, 255, 255, int(255 * opacity * alpha))) + if loading and ui_state.usbgpu_load_progress < 100: + pct_text = f"{ui_state.usbgpu_load_progress}%" + size = FONT_SIZES.max_speed + cell = measure_text_cached(self._font_bold, "0", size) + widths = [cell.x if c.isdigit() else measure_text_cached(self._font_bold, c, size).x for c in pct_text] + x = pos.x - 8 - sum(widths) + y = pos.y + (icon.height - cell.y) / 2 + for c, w in zip(pct_text, widths): + glyph = measure_text_cached(self._font_bold, c, size).x + rl.draw_text_ex(self._font_bold, c, rl.Vector2(x + (w - glyph) / 2, y), size, 0, rl.WHITE) + x += w + elif loading: + r = 14.0 + center = rl.Vector2(pos.x - 8 - r, pos.y + icon.height / 2) + start = (rl.get_time() * 360) % 360 + rl.draw_ring(center, r - 4, r, start, start + 270, 32, rl.WHITE) + def _draw_steering_wheel(self, rect: rl.Rectangle) -> None: wheel_txt = self._txt_wheel_critical if self._show_wheel_critical else self._txt_wheel diff --git a/openpilot/selfdrive/ui/ui_state.py b/openpilot/selfdrive/ui/ui_state.py index e0aca74ff2..425ebcad5c 100644 --- a/openpilot/selfdrive/ui/ui_state.py +++ b/openpilot/selfdrive/ui/ui_state.py @@ -86,6 +86,7 @@ class UIState(UIStateSP): self.usbgpu_compiled: bool = usbgpu_compiled() self.usbgpu_active: bool | None = self.params.get("UsbGpuActive") self.usbgpu_loading: bool = self.params.get_bool("UsbGpuLoading") + self.usbgpu_load_progress: int = self.params.get("UsbGpuLoadProgress", return_default=True) self.started: bool = False self.ignition: bool = False self.recording_audio: bool = False @@ -164,6 +165,9 @@ class UIState(UIStateSP): # Update started state self.started = self.sm["deviceState"].started and self.ignition + if self.usbgpu_loading: + self.usbgpu_load_progress = self.params.get("UsbGpuLoadProgress", return_default=True) + # Update body state if self.CP is not None and self.is_body != self.CP.notCar: self.is_body = self.CP.notCar diff --git a/openpilot/sunnypilot/modeld_v2/modeld.py b/openpilot/sunnypilot/modeld_v2/modeld.py index 9f3d709537..2faf9ace14 100755 --- a/openpilot/sunnypilot/modeld_v2/modeld.py +++ b/openpilot/sunnypilot/modeld_v2/modeld.py @@ -25,6 +25,7 @@ from opendbc.car.car_helpers import get_demo_car_params from tinygrad.tensor import Tensor from openpilot.common.file_chunker import open_file_chunked +from openpilot.selfdrive.modeld.load_progress import open_with_progress from openpilot.common.swaglog import cloudlog from openpilot.common.params import Params from openpilot.common.filter_simple import FirstOrderFilter @@ -107,7 +108,7 @@ class ModelState(ModelStateBase): def _init_combined(self, pkl_path, cam_w, cam_h, bundle): cloudlog.warning(f"loading combined pkl: {pkl_path}") - jits = load_oob(open_file_chunked(pkl_path)) + jits = load_oob(open_with_progress(pkl_path) if self.usbgpu else open_file_chunked(pkl_path)) self.WARP_DEV = 'QCOM' if COMMA_HARDWARE else 'CPU' self.DEV = 'AMD' if self.usbgpu else self.WARP_DEV @@ -333,6 +334,7 @@ def main(demo=False): params = Params() params.put_bool("UsbGpuLoading", USBGPU) + params.put("UsbGpuLoadProgress", 0) params.remove("UsbGpuActive") # visionipc clients