From 605dfaa1a952ea14010d53bbec5229901f270e4c Mon Sep 17 00:00:00 2001 From: Shane Smiskol Date: Sat, 2 May 2026 00:00:01 -0700 Subject: [PATCH] ui: fix torque bar points caching while scrolling (#37946) * ui: keep arc_bar_pts cache hot when bar is translated The LRU cache key included (cx, cy), which change every frame when the parent widget's rect translates (e.g. during a scroll animation in MainLayout). That made every cache lookup miss, recomputing the arc geometry twice per frame and dropping fps. Compute the shape at origin (so the cache key only depends on the geometry: radius, thickness, angles), then translate to (cx, cy) after. Cache stays hot under translation. Measured on comma four (engaged, scrolling): ~5fps recovered while moving between layouts. Co-Authored-By: Claude Opus 4.7 (1M context) * fully clean up * fully clean up --------- Co-authored-by: Claude Opus 4.7 (1M context) --- selfdrive/ui/mici/onroad/torque_bar.py | 26 +++++++++++++------------- 1 file changed, 13 insertions(+), 13 deletions(-) diff --git a/selfdrive/ui/mici/onroad/torque_bar.py b/selfdrive/ui/mici/onroad/torque_bar.py index 1338c8dfb3..406e6ff712 100644 --- a/selfdrive/ui/mici/onroad/torque_bar.py +++ b/selfdrive/ui/mici/onroad/torque_bar.py @@ -23,9 +23,9 @@ def quantized_lru_cache(maxsize=128): def decorator(func): cache = OrderedDict() @wraps(func) - def wrapper(cx, cy, r_mid, thickness, a0_deg, a1_deg, **kwargs): + def wrapper(r_mid, thickness, a0_deg, a1_deg, **kwargs): # Quantize inputs: balanced for smoothness vs cache effectiveness - key = (round(cx), round(cy), round(r_mid), + key = (round(r_mid), round(thickness), # 1px precision for smoother height transitions round(a0_deg * 10) / 10, # 0.1° precision for smoother angle transitions round(a1_deg * 10) / 10, @@ -37,7 +37,7 @@ def quantized_lru_cache(maxsize=128): if len(cache) >= maxsize: cache.popitem(last=False) - result = func(cx, cy, r_mid, thickness, a0_deg, a1_deg, **kwargs) + result = func(r_mid, thickness, a0_deg, a1_deg, **kwargs) cache[key] = result return cache[key] return wrapper @@ -45,12 +45,11 @@ def quantized_lru_cache(maxsize=128): @quantized_lru_cache(maxsize=256) -def arc_bar_pts(cx: float, cy: float, - r_mid: float, thickness: float, +def arc_bar_pts(r_mid: float, thickness: float, a0_deg: float, a1_deg: float, *, max_points: int = 100, cap_segs: int = 10, cap_radius: float = 7, px_per_seg: float = 2.0) -> np.ndarray: - """Return Nx2 np.float32 points for a single closed polygon (rounded thick arc).""" + """Return Nx2 np.float32 points for a single closed polygon (rounded thick arc), centered at origin.""" def get_cap(left: bool, a_deg: float): # end cap at a1: center (a1), sweep a1→a1+180 (skip endpoints to avoid dupes) @@ -59,7 +58,7 @@ def arc_bar_pts(cx: float, cy: float, nx, ny = math.cos(math.radians(a_deg)), math.sin(math.radians(a_deg)) # outward normal tx, ty = -ny, nx # tangent (CCW) - mx, my = cx + nx * r_mid, cy + ny * r_mid # mid-point at a1 + mx, my = nx * r_mid, ny * r_mid # mid-point at a1 if DEBUG: rl.draw_circle(int(mx), int(my), 4, rl.PURPLE) @@ -114,16 +113,16 @@ def arc_bar_pts(cx: float, cy: float, # outer arc a0→a1 ang_o = np.deg2rad(np.linspace(a0_deg, a1_deg, arc_segs + 1)) - outer = np.c_[cx + np.cos(ang_o) * (r_mid + half), - cy + np.sin(ang_o) * (r_mid + half)] + outer = np.c_[np.cos(ang_o) * (r_mid + half), + np.sin(ang_o) * (r_mid + half)] # end cap at a1 cap_end = get_cap(False, a1_deg) # inner arc a1→a0 ang_i = np.deg2rad(np.linspace(a1_deg, a0_deg, arc_segs + 1)) - inner = np.c_[cx + np.cos(ang_i) * (r_mid - half), - cy + np.sin(ang_i) * (r_mid - half)] + inner = np.c_[np.cos(ang_i) * (r_mid - half), + np.sin(ang_i) * (r_mid - half)] # start cap at a0 cap_start = get_cap(True, a0_deg) @@ -215,15 +214,16 @@ class TorqueBar(Widget): cx = rect.x + rect.width / 2 + 8 # offset 8px to right of camera feed cy = rect.y + rect.height + torque_line_radius - torque_line_offset + offset = np.array([cx, cy], dtype=np.float32) # draw bg torque indicator line - bg_pts = arc_bar_pts(cx, cy, mid_r, torque_line_height, torque_start_angle, torque_end_angle) + bg_pts = arc_bar_pts(mid_r, torque_line_height, torque_start_angle, torque_end_angle) + offset draw_polygon(rect, bg_pts, color=torque_line_bg_color) # draw torque indicator line a0s = top_angle a1s = a0s + torque_bg_angle_span / 2 * self._torque_filter.x - sl_pts = arc_bar_pts(cx, cy, mid_r, torque_line_height, a0s, a1s) + sl_pts = arc_bar_pts(mid_r, torque_line_height, a0s, a1s) + offset # draw beautiful gradient from center to 65% of the bg torque bar width start_grad_pt = cx / rect.width