From dda03d3655047c7610b31a7d5f2c90247b1eafa5 Mon Sep 17 00:00:00 2001 From: firestarsdog <229254897+firestarsdog@users.noreply.github.com> Date: Mon, 27 Jul 2026 13:27:33 -0400 Subject: [PATCH] Big UI: More optimization --- .../layouts/settings/starpilot/aethergrid.py | 25 +++++++++++++++++-- .../ui/layouts/settings/starpilot/panel.py | 2 +- .../ui/onroad/starpilot/slc_speed_limit.py | 17 +++++++++++-- .../onroad/starpilot/widget_layout_manager.py | 14 +++++++++++ selfdrive/ui/ui_state.py | 3 --- 5 files changed, 53 insertions(+), 8 deletions(-) diff --git a/selfdrive/ui/layouts/settings/starpilot/aethergrid.py b/selfdrive/ui/layouts/settings/starpilot/aethergrid.py index 5d3f33c76..f8ede9bb0 100644 --- a/selfdrive/ui/layouts/settings/starpilot/aethergrid.py +++ b/selfdrive/ui/layouts/settings/starpilot/aethergrid.py @@ -381,14 +381,30 @@ def point_hits(mouse_pos: MousePos, rect: rl.Rectangle, parent_rect: rl.Rectangl return hit.width > 0 and hit.height > 0 and rl.check_collision_point_rec(mouse_pos, hit) +_WRAP_TEXT_CACHE: dict[tuple[int, str, int, int, int], tuple[str, ...]] = {} +_MAX_WRAP_CACHE_SIZE = 512 + + def wrap_text(font: rl.Font, text: str, max_width: float, font_size: float, max_lines: int = 2) -> list[str]: + if not text or max_width <= 0: + return [] + + font_id = getattr(getattr(font, "texture", None), "id", id(font)) + w_key = int(round(max_width)) + s_key = int(round(font_size)) + key = (font_id, text, w_key, s_key, max_lines) + + cached = _WRAP_TEXT_CACHE.get(key) + if cached is not None: + return list(cached) + spacing = font_size * 0.15 words = text.split() lines: list[str] = [] current = "" for word in words: candidate = f"{current} {word}".strip() if current else word - if measure_text_cached(font, candidate, int(font_size), spacing=spacing).x <= max_width: + if measure_text_cached(font, candidate, s_key, spacing=spacing).x <= max_width: current = candidate else: if current: @@ -398,7 +414,12 @@ def wrap_text(font: rl.Font, text: str, max_width: float, font_size: float, max_ break if current and len(lines) < max_lines: lines.append(current) - return lines if lines else [text] + + result = tuple(lines if lines else [text]) + if len(_WRAP_TEXT_CACHE) >= _MAX_WRAP_CACHE_SIZE: + _WRAP_TEXT_CACHE.clear() + _WRAP_TEXT_CACHE[key] = result + return list(result) def build_list_panel_frame(rect: rl.Rectangle, metrics: AetherListMetrics = AETHER_LIST_METRICS) -> AetherListFrame: diff --git a/selfdrive/ui/layouts/settings/starpilot/panel.py b/selfdrive/ui/layouts/settings/starpilot/panel.py index 1e1c6a18d..6a7728ea8 100644 --- a/selfdrive/ui/layouts/settings/starpilot/panel.py +++ b/selfdrive/ui/layouts/settings/starpilot/panel.py @@ -107,7 +107,7 @@ class StarPilotPanelInfo: class StarPilotPanel(Widget): def __init__(self): super().__init__() - self._params_memory = Params(memory=True) + self._params_memory = ui_state.params_memory self._params = FrameCachedParams() self._navigate_callback: Callable | None = None self._back_callback: Callable | None = None diff --git a/selfdrive/ui/onroad/starpilot/slc_speed_limit.py b/selfdrive/ui/onroad/starpilot/slc_speed_limit.py index cf363f15a..2bea66953 100644 --- a/selfdrive/ui/onroad/starpilot/slc_speed_limit.py +++ b/selfdrive/ui/onroad/starpilot/slc_speed_limit.py @@ -111,13 +111,24 @@ def _speed_limit_pulse_color(base: rl.Color, alpha: int) -> rl.Color: ) -# ── State ───────────────────────────────────────────────────────────── +# ── State Cache ────────────────────────────────────────────────────── +_cached_key = None +_cached_slc_state = None + def _get_slc_state(): """Extract SLC state from SubMaster. Returns dict or None if stale/hidden.""" + global _cached_key, _cached_slc_state sm = ui_state.sm + key = (sm.frame, sm.recv_frame.get("starpilotPlan", 0), ui_state.started_frame) + if key == _cached_key: + return _cached_slc_state + + _cached_key = key + if sm.recv_frame["starpilotPlan"] < ui_state.started_frame: _reset_pulse() + _cached_slc_state = None return None plan = sm["starpilotPlan"] @@ -131,6 +142,7 @@ def _get_slc_state(): if not show_slc and not speed_limit_changed: _reset_pulse() + _cached_slc_state = None return None speed_conversion = CV.MS_TO_KPH if ui_state.is_metric else CV.MS_TO_MPH @@ -161,7 +173,7 @@ def _get_slc_state(): # and before any sign colors are computed downstream. _tick_pulse(plan.slcSpeedLimitSource, resolved_ms) - return { + _cached_slc_state = { 'speed_limit': speed_limit, 'speed_limit_str': "\u2013" if speed_limit <= 1 else str(int(round(speed_limit))), 'slc_overridden_speed': slc_overridden_speed, @@ -182,6 +194,7 @@ def _get_slc_state(): 'mapbox_sl': max(0.0, plan.slcMapboxSpeedLimit * speed_conversion), 'next_sl': max(0.0, plan.slcNextSpeedLimit * speed_conversion), } + return _cached_slc_state # ── Fonts ───────────────────────────────────────────────────────────── diff --git a/selfdrive/ui/onroad/starpilot/widget_layout_manager.py b/selfdrive/ui/onroad/starpilot/widget_layout_manager.py index 7d1513fde..a4dfe1d63 100644 --- a/selfdrive/ui/onroad/starpilot/widget_layout_manager.py +++ b/selfdrive/ui/onroad/starpilot/widget_layout_manager.py @@ -10,14 +10,28 @@ class WidgetLayoutManager: "right": [] } self.spacing = 15 # Spacing between widgets + self._prev_key = None def register_widget(self, zone: str, widget: LayoutWidget): """Register a widget in a specific zone.""" self.zones[zone].append(widget) self.zones[zone].sort(key=lambda w: w.priority) + self._prev_key = None def update_layout(self, content_rect: rl.Rectangle, is_rhd: bool = False): """Calculate and apply positions of all active widgets in all zones.""" + key = ( + content_rect.x, content_rect.y, content_rect.width, content_rect.height, + is_rhd, + tuple( + (zone, tuple((w, w.is_visible, w.get_size() if w.is_visible else None) for w in widgets)) + for zone, widgets in self.zones.items() + ) + ) + if key == self._prev_key: + return + self._prev_key = key + self.content_rect = content_rect self._layout_left() self._layout_bottom(is_rhd) diff --git a/selfdrive/ui/ui_state.py b/selfdrive/ui/ui_state.py index 76ef3d756..e7098c67c 100644 --- a/selfdrive/ui/ui_state.py +++ b/selfdrive/ui/ui_state.py @@ -16,8 +16,6 @@ from openpilot.system.hardware import HARDWARE, PC BACKLIGHT_OFFROAD = 65 if HARDWARE.get_device_type() == "mici" else 50 USBGPU_POLL_INTERVAL = 1.0 - - class CachedParams: def __init__(self, ttl: float = 1.0): self._params = Params() @@ -64,7 +62,6 @@ class CachedParams: return attr - class UIStatus(Enum): DISENGAGED = "disengaged" ENGAGED = "engaged"