raylib: background onboarding texture loading (#36343)

* this seems best so far

* better

* clean up

* debug

* debug

* clean up

* final
pull/33601/merge
Shane Smiskol 1 day ago committed by GitHub
parent 5f0e9fce61
commit 87443cd34d
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
  1. 40
      selfdrive/ui/layouts/onboarding.py
  2. 12
      system/ui/lib/application.py

@ -1,5 +1,6 @@
import os
import re
import threading
from enum import IntEnum
import pyray as rl
@ -38,15 +39,24 @@ class TrainingGuide(Widget):
self._completed_callback = completed_callback
self._step = 0
self._load_images()
self._load_image_paths()
def _load_images(self):
self._images = []
# Load first image now so we show something immediately
self._textures = [gui_app.texture(self._image_paths[0])]
self._image_objs = []
threading.Thread(target=self._preload_thread, daemon=True).start()
def _load_image_paths(self):
paths = [fn for fn in os.listdir(os.path.join(BASEDIR, "selfdrive/assets/training")) if re.match(r'^step\d*\.png$', fn)]
paths = sorted(paths, key=lambda x: int(re.search(r'\d+', x).group()))
for fn in paths:
path = os.path.join(BASEDIR, "selfdrive/assets/training", fn)
self._images.append(gui_app.texture(path))
self._image_paths = [os.path.join(BASEDIR, "selfdrive/assets/training", fn) for fn in paths]
def _preload_thread(self):
# PNG loading is slow in raylib, so we preload in a thread and upload to GPU in main thread
# We've already loaded the first image on init
for path in self._image_paths[1:]:
self._image_objs.append(gui_app._load_image_from_path(path))
def _handle_mouse_release(self, mouse_pos):
if rl.check_collision_point_rec(mouse_pos, STEP_RECTS[self._step]):
@ -57,30 +67,36 @@ class TrainingGuide(Widget):
ui_state.params.put_bool("RecordFront", yes)
# Restart training?
elif self._step == len(self._images) - 1:
elif self._step == len(self._image_paths) - 1:
if rl.check_collision_point_rec(mouse_pos, RESTART_TRAINING_RECT):
self._step = -1
self._step += 1
# Finished?
if self._step >= len(self._images):
if self._step >= len(self._image_paths):
self._step = 0
if self._completed_callback:
self._completed_callback()
def _update_state(self):
if len(self._image_objs):
self._textures.append(gui_app._load_texture_from_image(self._image_objs.pop(0)))
def _render(self, _):
rl.draw_texture(self._images[self._step], 0, 0, rl.WHITE)
# Safeguard against fast tapping
step = min(self._step, len(self._textures) - 1)
rl.draw_texture(self._textures[step], 0, 0, rl.WHITE)
# progress bar
if 0 < self._step < len(STEP_RECTS) - 1:
if 0 < step < len(STEP_RECTS) - 1:
h = 20
w = int((self._step / (len(STEP_RECTS) - 1)) * self._rect.width)
w = int((step / (len(STEP_RECTS) - 1)) * self._rect.width)
rl.draw_rectangle(int(self._rect.x), int(self._rect.y + self._rect.height - h),
w, h, rl.Color(70, 91, 234, 255))
if DEBUG:
rl.draw_rectangle_lines_ex(STEP_RECTS[self._step], 3, rl.RED)
rl.draw_rectangle_lines_ex(STEP_RECTS[step], 3, rl.RED)
return -1

@ -196,13 +196,14 @@ class GuiApplication:
return self._textures[cache_key]
with as_file(ASSETS_DIR.joinpath(asset_path)) as fspath:
texture_obj = self._load_texture_from_image(fspath.as_posix(), width, height, alpha_premultiply, keep_aspect_ratio)
image_obj = self._load_image_from_path(fspath.as_posix(), width, height, alpha_premultiply, keep_aspect_ratio)
texture_obj = self._load_texture_from_image(image_obj)
self._textures[cache_key] = texture_obj
return texture_obj
def _load_texture_from_image(self, image_path: str, width: int | None = None, height: int | None = None,
alpha_premultiply=False, keep_aspect_ratio=True):
"""Load and resize a texture, storing it for later automatic unloading."""
def _load_image_from_path(self, image_path: str, width: int | None = None, height: int | None = None,
alpha_premultiply: bool = False, keep_aspect_ratio: bool = True) -> rl.Image:
"""Load and resize an image, storing it for later automatic unloading."""
image = rl.load_image(image_path)
if alpha_premultiply:
@ -225,7 +226,10 @@ class GuiApplication:
rl.image_resize(image, new_width, new_height)
else:
rl.image_resize(image, width, height)
return image
def _load_texture_from_image(self, image: rl.Image) -> rl.Texture:
"""Send image to GPU and unload original image."""
texture = rl.load_texture_from_image(image)
# Set texture filtering to smooth the result
rl.set_texture_filter(texture, rl.TextureFilter.TEXTURE_FILTER_BILINEAR)

Loading…
Cancel
Save