Reapply "raylib: 20 FPS onroad (#36208)"

This reverts commit 5cbfc7705b.
rl-not-nice
Shane Smiskol 2 days ago
parent 19fc66f88a
commit ed185e90f6
  1. 11
      common/filter_simple.py
  2. 10
      selfdrive/ui/layouts/main.py
  3. 5
      selfdrive/ui/onroad/model_renderer.py
  4. 4
      selfdrive/ui/ui_state.py
  5. 19
      system/ui/lib/application.py
  6. 8
      system/ui/widgets/network.py

@ -1,16 +1,21 @@
class FirstOrderFilter: class FirstOrderFilter:
def __init__(self, x0, rc, dt, initialized=True): def __init__(self, x0, rc, dt, initialized=True):
self.x = x0 self.x = x0
self.dt = dt self._dt = dt
self.update_alpha(rc) self.update_alpha(rc)
self.initialized = initialized self.initialized = initialized
def update_dt(self, dt):
self._dt = dt
self.update_alpha(self._rc)
def update_alpha(self, rc): def update_alpha(self, rc):
self.alpha = self.dt / (rc + self.dt) self._rc = rc
self._alpha = self._dt / (self._rc + self._dt)
def update(self, x): def update(self, x):
if self.initialized: if self.initialized:
self.x = (1. - self.alpha) * self.x + self.alpha * x self.x = (1. - self._alpha) * self.x + self._alpha * x
else: else:
self.initialized = True self.initialized = True
self.x = x self.x = x

@ -1,6 +1,7 @@
import pyray as rl import pyray as rl
from enum import IntEnum from enum import IntEnum
import cereal.messaging as messaging import cereal.messaging as messaging
from openpilot.system.ui.lib.application import gui_app
from openpilot.selfdrive.ui.layouts.sidebar import Sidebar, SIDEBAR_WIDTH from openpilot.selfdrive.ui.layouts.sidebar import Sidebar, SIDEBAR_WIDTH
from openpilot.selfdrive.ui.layouts.home import HomeLayout from openpilot.selfdrive.ui.layouts.home import HomeLayout
from openpilot.selfdrive.ui.layouts.settings.settings import SettingsLayout, PanelType from openpilot.selfdrive.ui.layouts.settings.settings import SettingsLayout, PanelType
@ -9,6 +10,10 @@ from openpilot.selfdrive.ui.ui_state import device, ui_state
from openpilot.system.ui.widgets import Widget from openpilot.system.ui.widgets import Widget
ONROAD_FPS = 20
OFFROAD_FPS = 60
class MainState(IntEnum): class MainState(IntEnum):
HOME = 0 HOME = 0
SETTINGS = 1 SETTINGS = 1
@ -25,6 +30,8 @@ class MainLayout(Widget):
self._current_mode = MainState.HOME self._current_mode = MainState.HOME
self._prev_onroad = False self._prev_onroad = False
gui_app.set_target_fps(OFFROAD_FPS)
# Initialize layouts # Initialize layouts
self._layouts = {MainState.HOME: HomeLayout(), MainState.SETTINGS: SettingsLayout(), MainState.ONROAD: AugmentedRoadView()} self._layouts = {MainState.HOME: HomeLayout(), MainState.SETTINGS: SettingsLayout(), MainState.ONROAD: AugmentedRoadView()}
@ -74,6 +81,9 @@ class MainLayout(Widget):
self._current_mode = layout self._current_mode = layout
self._layouts[self._current_mode].show_event() self._layouts[self._current_mode].show_event()
# No need to draw onroad faster than source (model at 20Hz) and prevents screen tearing
gui_app.set_target_fps(ONROAD_FPS if self._current_mode == MainState.ONROAD else OFFROAD_FPS)
def open_settings(self, panel_type: PanelType): def open_settings(self, panel_type: PanelType):
self._layouts[MainState.SETTINGS].set_current_panel(panel_type) self._layouts[MainState.SETTINGS].set_current_panel(panel_type)
self._set_current_layout(MainState.SETTINGS) self._set_current_layout(MainState.SETTINGS)

@ -7,7 +7,7 @@ from openpilot.common.filter_simple import FirstOrderFilter
from openpilot.common.params import Params from openpilot.common.params import Params
from openpilot.selfdrive.locationd.calibrationd import HEIGHT_INIT from openpilot.selfdrive.locationd.calibrationd import HEIGHT_INIT
from openpilot.selfdrive.ui.ui_state import ui_state from openpilot.selfdrive.ui.ui_state import ui_state
from openpilot.system.ui.lib.application import DEFAULT_FPS from openpilot.system.ui.lib.application import gui_app
from openpilot.system.ui.lib.shader_polygon import draw_polygon from openpilot.system.ui.lib.shader_polygon import draw_polygon
from openpilot.system.ui.widgets import Widget from openpilot.system.ui.widgets import Widget
@ -48,7 +48,7 @@ class ModelRenderer(Widget):
super().__init__() super().__init__()
self._longitudinal_control = False self._longitudinal_control = False
self._experimental_mode = False self._experimental_mode = False
self._blend_filter = FirstOrderFilter(1.0, 0.25, 1 / DEFAULT_FPS) self._blend_filter = FirstOrderFilter(1.0, 0.25, 1 / gui_app.target_fps)
self._prev_allow_throttle = True self._prev_allow_throttle = True
self._lane_line_probs = np.zeros(4, dtype=np.float32) self._lane_line_probs = np.zeros(4, dtype=np.float32)
self._road_edge_stds = np.zeros(2, dtype=np.float32) self._road_edge_stds = np.zeros(2, dtype=np.float32)
@ -277,6 +277,7 @@ class ModelRenderer(Widget):
return return
allow_throttle = sm['longitudinalPlan'].allowThrottle or not self._longitudinal_control allow_throttle = sm['longitudinalPlan'].allowThrottle or not self._longitudinal_control
self._blend_filter.update_dt(1 / gui_app.target_fps)
self._blend_filter.update(int(allow_throttle)) self._blend_filter.update(int(allow_throttle))
if self._experimental_mode: if self._experimental_mode:

@ -9,7 +9,6 @@ from openpilot.common.filter_simple import FirstOrderFilter
from openpilot.common.params import Params, UnknownKeyName from openpilot.common.params import Params, UnknownKeyName
from openpilot.common.swaglog import cloudlog from openpilot.common.swaglog import cloudlog
from openpilot.selfdrive.ui.lib.prime_state import PrimeState from openpilot.selfdrive.ui.lib.prime_state import PrimeState
from openpilot.system.ui.lib.application import DEFAULT_FPS
from openpilot.system.hardware import HARDWARE from openpilot.system.hardware import HARDWARE
from openpilot.system.ui.lib.application import gui_app from openpilot.system.ui.lib.application import gui_app
@ -153,7 +152,7 @@ class Device:
self._offroad_brightness: int = BACKLIGHT_OFFROAD self._offroad_brightness: int = BACKLIGHT_OFFROAD
self._last_brightness: int = 0 self._last_brightness: int = 0
self._brightness_filter = FirstOrderFilter(BACKLIGHT_OFFROAD, 10.00, 1 / DEFAULT_FPS) self._brightness_filter = FirstOrderFilter(BACKLIGHT_OFFROAD, 10.00, 1 / gui_app.target_fps)
self._brightness_thread: threading.Thread | None = None self._brightness_thread: threading.Thread | None = None
def reset_interactive_timeout(self, timeout: int = -1) -> None: def reset_interactive_timeout(self, timeout: int = -1) -> None:
@ -190,6 +189,7 @@ class Device:
clipped_brightness = float(np.clip(100 * clipped_brightness, 10, 100)) clipped_brightness = float(np.clip(100 * clipped_brightness, 10, 100))
self._brightness_filter.update_dt(1 / gui_app.target_fps)
brightness = round(self._brightness_filter.update(clipped_brightness)) brightness = round(self._brightness_filter.update(clipped_brightness))
if not self._awake: if not self._awake:
brightness = 0 brightness = 0

@ -11,10 +11,10 @@ from enum import StrEnum
from typing import NamedTuple from typing import NamedTuple
from importlib.resources import as_file, files from importlib.resources import as_file, files
from openpilot.common.swaglog import cloudlog from openpilot.common.swaglog import cloudlog
from openpilot.system.hardware import HARDWARE, PC, TICI from openpilot.system.hardware import HARDWARE, PC
from openpilot.common.realtime import Ratekeeper from openpilot.common.realtime import Ratekeeper
DEFAULT_FPS = int(os.getenv("FPS", 20 if TICI else 60)) _DEFAULT_FPS = int(os.getenv("FPS", "60"))
FPS_LOG_INTERVAL = 5 # Seconds between logging FPS drops FPS_LOG_INTERVAL = 5 # Seconds between logging FPS drops
FPS_DROP_THRESHOLD = 0.9 # FPS drop threshold for triggering a warning FPS_DROP_THRESHOLD = 0.9 # FPS drop threshold for triggering a warning
FPS_CRITICAL_THRESHOLD = 0.5 # Critical threshold for triggering strict actions FPS_CRITICAL_THRESHOLD = 0.5 # Critical threshold for triggering strict actions
@ -130,7 +130,7 @@ class GuiApplication:
self._scaled_height = int(self._height * self._scale) self._scaled_height = int(self._height * self._scale)
self._render_texture: rl.RenderTexture | None = None self._render_texture: rl.RenderTexture | None = None
self._textures: dict[str, rl.Texture] = {} self._textures: dict[str, rl.Texture] = {}
self._target_fps: int = DEFAULT_FPS self._target_fps: int = _DEFAULT_FPS
self._last_fps_log_time: float = time.monotonic() self._last_fps_log_time: float = time.monotonic()
self._window_close_requested = False self._window_close_requested = False
self._trace_log_callback = None self._trace_log_callback = None
@ -145,7 +145,7 @@ class GuiApplication:
def request_close(self): def request_close(self):
self._window_close_requested = True self._window_close_requested = True
def init_window(self, title: str, fps: int = DEFAULT_FPS): def init_window(self, title: str, fps: int = _DEFAULT_FPS):
atexit.register(self.close) # Automatically call close() on exit atexit.register(self.close) # Automatically call close() on exit
HARDWARE.set_display_power(True) HARDWARE.set_display_power(True)
@ -164,15 +164,22 @@ class GuiApplication:
rl.set_mouse_scale(1 / self._scale, 1 / self._scale) rl.set_mouse_scale(1 / self._scale, 1 / self._scale)
self._render_texture = rl.load_render_texture(self._width, self._height) self._render_texture = rl.load_render_texture(self._width, self._height)
rl.set_texture_filter(self._render_texture.texture, rl.TextureFilter.TEXTURE_FILTER_BILINEAR) rl.set_texture_filter(self._render_texture.texture, rl.TextureFilter.TEXTURE_FILTER_BILINEAR)
rl.set_target_fps(fps)
self._target_fps = fps self.set_target_fps(fps)
self._set_styles() self._set_styles()
self._load_fonts() self._load_fonts()
if not PC: if not PC:
self._mouse.start() self._mouse.start()
@property
def target_fps(self):
return self._target_fps
def set_target_fps(self, fps: int):
self._target_fps = fps
rl.set_target_fps(fps)
def set_modal_overlay(self, overlay, callback: Callable | None = None): def set_modal_overlay(self, overlay, callback: Callable | None = None):
self._modal_overlay = ModalOverlay(overlay=overlay, callback=callback) self._modal_overlay = ModalOverlay(overlay=overlay, callback=callback)

@ -5,7 +5,7 @@ from typing import cast
import pyray as rl import pyray as rl
from openpilot.common.filter_simple import FirstOrderFilter from openpilot.common.filter_simple import FirstOrderFilter
from openpilot.common.params import Params from openpilot.common.params import Params
from openpilot.system.ui.lib.application import gui_app, DEFAULT_FPS from openpilot.system.ui.lib.application import gui_app
from openpilot.system.ui.lib.scroll_panel import GuiScrollPanel from openpilot.system.ui.lib.scroll_panel import GuiScrollPanel
from openpilot.system.ui.lib.wifi_manager import WifiManager, SecurityType, Network, MeteredType from openpilot.system.ui.lib.wifi_manager import WifiManager, SecurityType, Network, MeteredType
from openpilot.system.ui.widgets import Widget from openpilot.system.ui.widgets import Widget
@ -50,10 +50,12 @@ class NavButton(Widget):
super().__init__() super().__init__()
self.text = text self.text = text
self.set_rect(rl.Rectangle(0, 0, 400, 100)) self.set_rect(rl.Rectangle(0, 0, 400, 100))
self._x_pos_filter = FirstOrderFilter(0.0, 0.05, 1 / DEFAULT_FPS, initialized=False) self._x_pos_filter = FirstOrderFilter(0.0, 0.05, 1 / gui_app.target_fps, initialized=False)
self._y_pos_filter = FirstOrderFilter(0.0, 0.05, 1 / DEFAULT_FPS, initialized=False) self._y_pos_filter = FirstOrderFilter(0.0, 0.05, 1 / gui_app.target_fps, initialized=False)
def set_position(self, x: float, y: float) -> None: def set_position(self, x: float, y: float) -> None:
self._x_pos_filter.update_dt(1 / gui_app.target_fps)
self._y_pos_filter.update_dt(1 / gui_app.target_fps)
x = self._x_pos_filter.update(x) x = self._x_pos_filter.update(x)
y = self._y_pos_filter.update(y) y = self._y_pos_filter.update(y)
changed = (self._rect.x != x or self._rect.y != y) changed = (self._rect.x != x or self._rect.y != y)

Loading…
Cancel
Save