Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
21 commits
Select commit Hold shift + click to select a range
2e2ac59
[experiment] ui on the CPU
adeebshihadeh Jul 26, 2026
4542d28
ci: probe display target hardware
adeebshihadeh Jul 26, 2026
747524c
ci: run display probe on PR test branch
adeebshihadeh Jul 26, 2026
c52ed56
ci: inspect MICI slot controls
adeebshihadeh Jul 26, 2026
f0a119f
ci: query MICI slot control usage
adeebshihadeh Jul 26, 2026
8a98b2c
ci: test native landscape display on inactive MICI slot
adeebshihadeh Jul 26, 2026
8d6b5c2
ci: inspect MICI slot implementation
adeebshihadeh Jul 26, 2026
9e7b2d5
ci: remove MICI display experiment scaffolding
adeebshihadeh Jul 26, 2026
58d75ca
ci: reserve offline MICI device
adeebshihadeh Jul 26, 2026
9a98f4e
ci: fix resource reservation helper
adeebshihadeh Jul 26, 2026
85a2e5b
ci: lock offline MICI device
adeebshihadeh Jul 26, 2026
245c1fe
ci: remove MICI lock scaffolding
adeebshihadeh Jul 26, 2026
c0ad9e8
Use MDP for MICI UI composition
adeebshihadeh Jul 26, 2026
b6b29ef
Mark CPU renderer prototype executable
adeebshihadeh Jul 26, 2026
3f72362
Render MICI UI directly into cached scanout buffers
adeebshihadeh Jul 26, 2026
8e834aa
Vectorize CPU UI alpha blending with NEON
adeebshihadeh Jul 26, 2026
91cc750
Cull transparent CPU raster work
adeebshihadeh Jul 26, 2026
a081a35
Speed up CPU ring rasterization
adeebshihadeh Jul 27, 2026
a3582b7
Merge remote-tracking branch 'origin/master' into ui_on_the_cpu
adeebshihadeh Jul 27, 2026
87a04e6
Clean up CPU UI backend
adeebshihadeh Jul 27, 2026
0500220
Require MDP camera composition
adeebshihadeh Jul 27, 2026
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
12 changes: 12 additions & 0 deletions openpilot/selfdrive/ui/SConscript
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,18 @@ import importlib.util

Import('env', 'arch', 'common')

# Native core for the GPU-free MICI UI backend. It deliberately has no GL/EGL
# dependencies and is built on Linux hosts so PC parity tests exercise the same
# code that runs on device.
if arch != "Darwin":
cpu_renderer_env = env.Clone()
cpu_renderer_env["CPPPATH"] += ["/usr/include/libdrm"]
cpu_renderer_env.SharedLibrary(
"#openpilot/system/ui/lib/cpu_renderer/libraylib_cpu",
["#openpilot/system/ui/lib/cpu_renderer/renderer.c"],
LIBS=["m", "drm"],
)

# build the fonts
generator = File("#openpilot/selfdrive/assets/fonts/process.py")
source_files = Glob("#openpilot/selfdrive/assets/fonts/*.ttf") + Glob("#openpilot/selfdrive/assets/fonts/*.otf")
Expand Down
2 changes: 1 addition & 1 deletion openpilot/selfdrive/ui/body/layouts/onroad.py
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import time
import pyray as rl
from openpilot.system.ui.lib import raylib as rl

from openpilot.system.ui.lib.application import gui_app, FontWeight
from openpilot.system.ui.widgets import Widget
Expand Down
2 changes: 1 addition & 1 deletion openpilot/selfdrive/ui/mici/layouts/home.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@
import time

from openpilot.cereal import log
import pyray as rl
from openpilot.system.ui.lib import raylib as rl
from collections.abc import Callable
from openpilot.system.ui.widgets import Widget
from openpilot.system.ui.widgets.layouts import HBoxLayout
Expand Down
2 changes: 1 addition & 1 deletion openpilot/selfdrive/ui/mici/layouts/main.py
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import pyray as rl
from openpilot.system.ui.lib import raylib as rl
import openpilot.cereal.messaging as messaging
from openpilot.selfdrive.ui.mici.layouts.home import MiciHomeLayout
from openpilot.selfdrive.ui.mici.layouts.settings.settings import SettingsLayout
Expand Down
2 changes: 1 addition & 1 deletion openpilot/selfdrive/ui/mici/layouts/offroad_alerts.py
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import pyray as rl
from openpilot.system.ui.lib import raylib as rl
import re
import threading
import time
Expand Down
2 changes: 1 addition & 1 deletion openpilot/selfdrive/ui/mici/layouts/onboarding.py
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
import math
import numpy as np
import qrcode
import pyray as rl
from openpilot.system.ui.lib import raylib as rl
from collections.abc import Callable
from openpilot.common.filter_simple import FirstOrderFilter
from openpilot.system.ui.lib.application import FontWeight, gui_app
Expand Down
2 changes: 1 addition & 1 deletion openpilot/selfdrive/ui/mici/layouts/settings/device.py
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import os
import pyray as rl
from openpilot.system.ui.lib import raylib as rl
from collections.abc import Callable

from openpilot.common.basedir import BASEDIR
Expand Down
2 changes: 1 addition & 1 deletion openpilot/selfdrive/ui/mici/layouts/settings/firehose.py
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
import requests
import threading
import time
import pyray as rl
from openpilot.system.ui.lib import raylib as rl

from openpilot.common.api import api_get
from openpilot.common.params import Params
Expand Down
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import pyray as rl
from openpilot.system.ui.lib import raylib as rl

from openpilot.selfdrive.ui.mici.layouts.settings.network.wifi_ui import WifiIcon
from openpilot.selfdrive.ui.mici.widgets.button import BigButton
Expand Down
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import math
import numpy as np
import pyray as rl
from openpilot.system.ui.lib import raylib as rl
from collections.abc import Callable

from openpilot.common.swaglog import cloudlog
Expand Down
2 changes: 1 addition & 1 deletion openpilot/selfdrive/ui/mici/layouts/settings/software.py
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import subprocess
import threading
import pyray as rl
from openpilot.system.ui.lib import raylib as rl
from enum import IntEnum
from collections.abc import Callable

Expand Down
2 changes: 1 addition & 1 deletion openpilot/selfdrive/ui/mici/onroad/__init__.py
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import pyray as rl
from openpilot.system.ui.lib import raylib as rl

SIDE_PANEL_WIDTH = 60

Expand Down
2 changes: 1 addition & 1 deletion openpilot/selfdrive/ui/mici/onroad/alert_renderer.py
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
import time
from enum import StrEnum
from typing import NamedTuple
import pyray as rl
from openpilot.system.ui.lib import raylib as rl
import random
import string
from dataclasses import dataclass
Expand Down
2 changes: 1 addition & 1 deletion openpilot/selfdrive/ui/mici/onroad/augmented_road_view.py
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import numpy as np
import pyray as rl
from openpilot.system.ui.lib import raylib as rl
from openpilot.cereal import log
from opendbc.car.structs import car
from msgq.visionipc import VisionStreamType
Expand Down
31 changes: 19 additions & 12 deletions openpilot/selfdrive/ui/mici/onroad/cameraview.py
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import platform
import numpy as np
import pyray as rl
from openpilot.system.ui.lib import raylib as rl

from msgq.visionipc import VisionIpcClient, VisionStreamType, VisionBuf
from openpilot.common.swaglog import cloudlog
Expand Down Expand Up @@ -120,12 +120,14 @@ def __init__(self, name: str, stream_type: VisionStreamType):

self._texture_needs_update = True
self.last_connection_attempt: float = 0.0
self.shader = rl.load_shader_from_memory(VERTEX_SHADER, FRAME_FRAGMENT_SHADER)
self._texture1_loc: int = rl.get_shader_location(self.shader, "texture1") if not TICI else -1
self._engaged_loc = rl.get_shader_location(self.shader, "engaged")
self._engaged_val = rl.ffi.new("int[1]", [1])
self._enhance_driver_loc = rl.get_shader_location(self.shader, "enhance_driver")
self._enhance_driver_val = rl.ffi.new("int[1]", [1 if stream_type == VisionStreamType.VISION_STREAM_DRIVER else 0])
self.shader = rl.Shader()
if not rl.using_cpu_backend():
self.shader = rl.load_shader_from_memory(VERTEX_SHADER, FRAME_FRAGMENT_SHADER)
self._texture1_loc: int = rl.get_shader_location(self.shader, "texture1") if not TICI else -1
self._engaged_loc = rl.get_shader_location(self.shader, "engaged")
self._engaged_val = rl.ffi.new("int[1]", [1])
self._enhance_driver_loc = rl.get_shader_location(self.shader, "enhance_driver")
self._enhance_driver_val = rl.ffi.new("int[1]", [1 if stream_type == VisionStreamType.VISION_STREAM_DRIVER else 0])

self.frame: VisionBuf | None = None
self.texture_y: rl.Texture | None = None
Expand All @@ -138,7 +140,7 @@ def __init__(self, name: str, stream_type: VisionStreamType):
self._placeholder_color: rl.Color | None = None

# Initialize EGL for zero-copy rendering on TICI
if TICI:
if TICI and not rl.using_cpu_backend():
if not init_egl():
raise RuntimeError("Failed to initialize EGL")

Expand Down Expand Up @@ -185,12 +187,12 @@ def close(self) -> None:
self._clear_textures()

# Clean up EGL texture
if TICI and self.egl_texture:
if TICI and not rl.using_cpu_backend() and self.egl_texture:
rl.unload_texture(self.egl_texture)
self.egl_texture = None

# Clean up shader
if self.shader and self.shader.id:
if self.shader.id:
rl.unload_shader(self.shader)
self.shader.id = 0

Expand Down Expand Up @@ -260,7 +262,10 @@ def _render(self, rect: rl.Rectangle, /):
dst_rect = rl.Rectangle(x_offset, y_offset, scale_x, scale_y)

# Render with appropriate method
if TICI:
if rl.using_cpu_backend():
rl.draw_nv12(self.frame, dst_rect, ui_state.status != UIStatus.DISENGAGED,
self._stream_type == VisionStreamType.VISION_STREAM_DRIVER)
elif TICI:
self._render_egl(src_rect, dst_rect)
else:
self._render_textures(src_rect, dst_rect)
Expand Down Expand Up @@ -384,6 +389,8 @@ def _complete_switch(self) -> None:

def _initialize_textures(self):
self._clear_textures()
if rl.using_cpu_backend():
return
if not TICI:
self.texture_y = rl.load_texture_from_image(rl.Image(None, int(self.client.stride),
int(self.client.height), 1, rl.PixelFormat.PIXELFORMAT_UNCOMPRESSED_GRAYSCALE))
Expand All @@ -400,7 +407,7 @@ def _clear_textures(self):
self.texture_uv = None

# Clean up EGL resources
if TICI:
if TICI and not rl.using_cpu_backend():
for data in self.egl_images.values():
destroy_egl_image(data)
self.egl_images = {}
Expand Down
2 changes: 1 addition & 1 deletion openpilot/selfdrive/ui/mici/onroad/confidence_ball.py
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import math
import pyray as rl
from openpilot.system.ui.lib import raylib as rl
from openpilot.selfdrive.ui.mici.onroad import SIDE_PANEL_WIDTH
from openpilot.selfdrive.ui.ui_state import ui_state, UIStatus
from openpilot.system.ui.widgets import Widget
Expand Down
2 changes: 1 addition & 1 deletion openpilot/selfdrive/ui/mici/onroad/driver_camera_dialog.py
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import pyray as rl
from openpilot.system.ui.lib import raylib as rl
from openpilot.cereal import log, messaging
from msgq.visionipc import VisionStreamType
from openpilot.selfdrive.ui.mici.onroad.cameraview import CameraView
Expand Down
2 changes: 1 addition & 1 deletion openpilot/selfdrive/ui/mici/onroad/driver_state.py
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import pyray as rl
from openpilot.system.ui.lib import raylib as rl
import numpy as np
import math
from openpilot.cereal import log
Expand Down
2 changes: 1 addition & 1 deletion openpilot/selfdrive/ui/mici/onroad/hud_renderer.py
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import pyray as rl
from openpilot.system.ui.lib import raylib as rl
from dataclasses import dataclass
from openpilot.common.constants import CV
from openpilot.selfdrive.ui.mici.onroad.torque_bar import TorqueBar
Expand Down
2 changes: 1 addition & 1 deletion openpilot/selfdrive/ui/mici/onroad/model_renderer.py
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import colorsys
import numpy as np
import pyray as rl
from openpilot.system.ui.lib import raylib as rl
from openpilot.cereal import messaging
from opendbc.car.structs import car
from dataclasses import dataclass, field
Expand Down
2 changes: 1 addition & 1 deletion openpilot/selfdrive/ui/mici/onroad/torque_bar.py
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@
from collections import OrderedDict

import numpy as np
import pyray as rl
from openpilot.system.ui.lib import raylib as rl
from opendbc.car import ACCELERATION_DUE_TO_GRAVITY
from openpilot.selfdrive.ui.mici.onroad import blend_colors
from openpilot.selfdrive.ui.ui_state import ui_state, UIStatus
Expand Down
2 changes: 1 addition & 1 deletion openpilot/selfdrive/ui/mici/widgets/button.py
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import math
import pyray as rl
from openpilot.system.ui.lib import raylib as rl
from typing import TYPE_CHECKING, Union
from enum import Enum
from collections.abc import Callable
Expand Down
2 changes: 1 addition & 1 deletion openpilot/selfdrive/ui/mici/widgets/dialog.py
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import abc
import math
import pyray as rl
from openpilot.system.ui.lib import raylib as rl
from typing import Union
from collections.abc import Callable
from openpilot.system.ui.widgets.nav_widget import NavWidget
Expand Down
2 changes: 1 addition & 1 deletion openpilot/selfdrive/ui/mici/widgets/pairing_dialog.py
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import pyray as rl
from openpilot.system.ui.lib import raylib as rl
import qrcode
import numpy as np
import time
Expand Down
4 changes: 2 additions & 2 deletions openpilot/selfdrive/ui/tests/profile_onroad.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@
import os
import time
import cProfile
import pyray as rl
from openpilot.system.ui.lib import raylib as rl
import numpy as np

from msgq.visionipc import VisionIpcServer, VisionStreamType
Expand Down Expand Up @@ -76,7 +76,7 @@ def mock_update(timeout=None):
if args.max_seconds:
message_chunks = message_chunks[:int(args.max_seconds * FPS)]

print("Initializing UI with GPU rendering...")
print(f"Initializing UI with {'CPU' if rl.using_cpu_backend() else 'GPU'} rendering...")

if args.headless:
os.environ['SDL_VIDEODRIVER'] = 'dummy'
Expand Down
Loading
Loading