system/ui: add SCALE env var (#35272)

* ui: add SCALE env var

* linting

* cleaner

---------

Co-authored-by: Cameron Clough <cameronjclough@gmail.com>
pull/35273/head
Adeeb Shihadeh 7 days ago committed by GitHub
parent f630cac06f
commit ed0f812995
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
  1. 1
      system/ui/README.md
  2. 31
      system/ui/lib/application.py

@ -5,5 +5,6 @@ The user interfaces here are built with [raylib](https://www.raylib.com/).
Quick start: Quick start:
* set `DEBUG_FPS=1` to show the FPS * set `DEBUG_FPS=1` to show the FPS
* set `STRICT_MODE=1` to kill the app if it drops too much below 60fps * set `STRICT_MODE=1` to kill the app if it drops too much below 60fps
* set `SCALE=1.5` to scale the entire UI by 1.5x
* https://www.raylib.com/cheatsheet/cheatsheet.html * https://www.raylib.com/cheatsheet/cheatsheet.html
* https://electronstudio.github.io/raylib-python-cffi/README.html#quickstart * https://electronstudio.github.io/raylib-python-cffi/README.html#quickstart

@ -15,6 +15,7 @@ FPS_CRITICAL_THRESHOLD = 0.5 # Critical threshold for triggering strict actions
ENABLE_VSYNC = os.getenv("ENABLE_VSYNC") == "1" ENABLE_VSYNC = os.getenv("ENABLE_VSYNC") == "1"
DEBUG_FPS = os.getenv("DEBUG_FPS") == '1' DEBUG_FPS = os.getenv("DEBUG_FPS") == '1'
STRICT_MODE = os.getenv("STRICT_MODE") == '1' STRICT_MODE = os.getenv("STRICT_MODE") == '1'
SCALE = float(os.getenv("SCALE", "1.0"))
DEFAULT_TEXT_SIZE = 60 DEFAULT_TEXT_SIZE = 60
DEFAULT_TEXT_COLOR = rl.WHITE DEFAULT_TEXT_COLOR = rl.WHITE
@ -22,6 +23,7 @@ DEFAULT_TEXT_COLOR = rl.WHITE
ASSETS_DIR = files("openpilot.selfdrive").joinpath("assets") ASSETS_DIR = files("openpilot.selfdrive").joinpath("assets")
FONT_DIR = ASSETS_DIR.joinpath("fonts") FONT_DIR = ASSETS_DIR.joinpath("fonts")
class FontWeight(IntEnum): class FontWeight(IntEnum):
THIN = 0 THIN = 0
EXTRA_LIGHT = 1 EXTRA_LIGHT = 1
@ -39,6 +41,10 @@ class GuiApplication:
self._fonts: dict[FontWeight, rl.Font] = {} self._fonts: dict[FontWeight, rl.Font] = {}
self._width = width self._width = width
self._height = height self._height = height
self._scale = SCALE
self._scaled_width = int(self._width * self._scale)
self._scaled_height = int(self._height * self._scale)
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()
@ -62,14 +68,15 @@ class GuiApplication:
flags |= rl.ConfigFlags.FLAG_VSYNC_HINT flags |= rl.ConfigFlags.FLAG_VSYNC_HINT
rl.set_config_flags(flags) rl.set_config_flags(flags)
rl.init_window(self._width, self._height, title) rl.init_window(self._scaled_width, self._scaled_height, title)
if self._scale != 1.0:
self._render_texture = rl.load_render_texture(self._width, self._height)
rl.set_target_fps(fps) rl.set_target_fps(fps)
self._target_fps = fps self._target_fps = fps
self._set_styles() self._set_styles()
self._load_fonts() self._load_fonts()
def texture(self, asset_path: str, width: int, height: int, alpha_premultiply=False, keep_aspect_ratio=True): def texture(self, asset_path: str, width: int, height: int, alpha_premultiply=False, keep_aspect_ratio=True):
cache_key = f"{asset_path}_{width}_{height}_{alpha_premultiply}{keep_aspect_ratio}" cache_key = f"{asset_path}_{width}_{height}_{alpha_premultiply}{keep_aspect_ratio}"
if cache_key in self._textures: if cache_key in self._textures:
@ -80,7 +87,7 @@ class GuiApplication:
self._textures[cache_key] = texture_obj self._textures[cache_key] = texture_obj
return texture_obj return texture_obj
def _load_texture_from_image(self, image_path: str, width: int, height: int, alpha_premultiply = False, keep_aspect_ratio=True): def _load_texture_from_image(self, image_path: str, width: int, height: int, alpha_premultiply=False, keep_aspect_ratio=True):
"""Load and resize a texture, storing it for later automatic unloading.""" """Load and resize a texture, storing it for later automatic unloading."""
image = rl.load_image(image_path) image = rl.load_image(image_path)
@ -123,16 +130,32 @@ class GuiApplication:
rl.unload_font(font) rl.unload_font(font)
self._fonts = {} self._fonts = {}
if self._render_texture is not None:
rl.unload_render_texture(self._render_texture)
self._render_texture = None
rl.close_window() rl.close_window()
def render(self): def render(self):
try: try:
while not (self._window_close_requested or rl.window_should_close()): while not (self._window_close_requested or rl.window_should_close()):
if self._render_texture:
rl.begin_texture_mode(self._render_texture)
rl.clear_background(rl.BLACK)
else:
rl.begin_drawing() rl.begin_drawing()
rl.clear_background(rl.BLACK) rl.clear_background(rl.BLACK)
yield yield
if self._render_texture:
rl.end_texture_mode()
rl.begin_drawing()
rl.clear_background(rl.BLACK)
src_rect = rl.Rectangle(0, 0, float(self._width), -float(self._height))
dst_rect = rl.Rectangle(0, 0, float(self._scaled_width), float(self._scaled_height))
rl.draw_texture_pro(self._render_texture.texture, src_rect, dst_rect, rl.Vector2(0, 0), 0.0, rl.WHITE)
if DEBUG_FPS: if DEBUG_FPS:
rl.draw_fps(10, 10) rl.draw_fps(10, 10)
@ -141,7 +164,7 @@ class GuiApplication:
except KeyboardInterrupt: except KeyboardInterrupt:
pass pass
def font(self, font_weight: FontWeight=FontWeight.NORMAL): def font(self, font_weight: FontWeight = FontWeight.NORMAL):
return self._fonts[font_weight] return self._fonts[font_weight]
@property @property

Loading…
Cancel
Save