From 7d2f012387bd7e7271232f4ecbc8574622ca551b Mon Sep 17 00:00:00 2001 From: firestarsdog <229254897+firestarsdog@users.noreply.github.com> Date: Mon, 28 Sep 2026 00:44:41 -0400 Subject: [PATCH] model renderer optimization --- selfdrive/ui/onroad/model_renderer.py | 25 ++++++------ selfdrive/ui/tests/test_model_renderer.py | 47 +++++++++++++++++++++++ 2 files changed, 61 insertions(+), 11 deletions(-) diff --git a/selfdrive/ui/onroad/model_renderer.py b/selfdrive/ui/onroad/model_renderer.py index 73bfef6b7f..0e99c37d50 100644 --- a/selfdrive/ui/onroad/model_renderer.py +++ b/selfdrive/ui/onroad/model_renderer.py @@ -148,23 +148,26 @@ class ModelRenderer(Widget): self._rainbow_path.update(max(sm['carState'].vEgo, 0.0)) render_lead_indicator = self._should_render_lead_indicator(radar_state) - # Update model data when needed + # Update model and lead data model_updated = sm.updated['modelV2'] - if model_updated or sm.updated['radarState'] or self._transform_dirty: - if model_updated: - self._update_raw_points(model) + transform_dirty = self._transform_dirty - path_x_array = self._path.raw_points[:, 0] - if path_x_array.size == 0: - return + if model_updated: + self._update_raw_points(model) + path_x_array = self._path.raw_points[:, 0] + if path_x_array.size == 0: + return + + if model_updated or transform_dirty: self._update_model(lead_one, path_x_array) - if render_lead_indicator: - self._update_leads(radar_state, path_x_array) - if sm.valid.get("starpilotRadarState", False): - self._update_adjacent_leads(sm["starpilotRadarState"], path_x_array) self._transform_dirty = False + if render_lead_indicator and (sm.updated['radarState'] or model_updated or transform_dirty): + self._update_leads(radar_state, path_x_array) + if sm.valid.get("starpilotRadarState", False): + self._update_adjacent_leads(sm["starpilotRadarState"], path_x_array) + self._lead_text_rects = [] self._adjacent_lead_text_rects = [] diff --git a/selfdrive/ui/tests/test_model_renderer.py b/selfdrive/ui/tests/test_model_renderer.py index 341d327cb3..912525fb3d 100644 --- a/selfdrive/ui/tests/test_model_renderer.py +++ b/selfdrive/ui/tests/test_model_renderer.py @@ -161,3 +161,50 @@ def test_clipping_region_bounds_parity(renderer): for b, i in zip(batched, individual, strict=True): assert b.shape == i.shape np.testing.assert_allclose(b, i, rtol=1e-5, atol=1e-3) + + +def test_radar_update_decoupled_from_model_regeneration(monkeypatch): + from unittest.mock import MagicMock + import pyray as rl + from openpilot.selfdrive.ui.onroad.model_renderer import ModelPoints + + r = object.__new__(ModelRenderer) + r._path = ModelPoints() + r._path.raw_points = np.zeros((10, 3), dtype=np.float32) + r._transform_dirty = False + r._started_frame = 0 + r._should_render_lead_indicator = lambda rs: True + r._update_model = MagicMock() + r._update_leads = MagicMock() + r._update_adjacent_leads = MagicMock() + r._draw_lane_lines = MagicMock() + r._draw_path = MagicMock() + r._draw_lead_indicator = MagicMock() + r._draw_radar_tracks = MagicMock() + r._update_raw_points = MagicMock() + r._params = SimpleNamespace(get_bool=lambda *args, **kwargs: False) + + from openpilot.selfdrive.ui.ui_state import ui_state + class MockSM: + recv_frame = {"liveCalibration": 1, "modelV2": 1} + updated = {"carParams": False, "modelV2": False, "radarState": True} + valid = {"radarState": True, "starpilotRadarState": False} + def __getitem__(self, k): + return SimpleNamespace(openpilotLongitudinalControl=False, experimentalMode=False, leadOne=None, position=None, height=[1.22]) + + mock_sm = MockSM() + monkeypatch.setattr(ui_state, "sm", mock_sm) + monkeypatch.setattr(ui_state, "started_frame", 0) + + # Frame 1: Only radarState updated. _update_model must NOT run, but _update_leads MUST run. + r._render(rl.Rectangle(0, 0, 100, 100)) + assert not r._update_model.called, "Model geometry should not reproject on radarState alone" + assert r._update_leads.called, "Lead indicators should update on radarState" + + # Frame 2: modelV2 updated. _update_model MUST run. + mock_sm.updated["modelV2"] = True + mock_sm.updated["radarState"] = False + r._update_model.reset_mock() + r._render(rl.Rectangle(0, 0, 100, 100)) + assert r._update_model.called, "Model geometry should reproject when modelV2 updates" +