diff --git a/common/libcommon.a b/common/libcommon.a index 26cf3ca29..081f605d1 100644 Binary files a/common/libcommon.a and b/common/libcommon.a differ diff --git a/common/params_keys.h b/common/params_keys.h index 641f91e60..605fb09c0 100644 --- a/common/params_keys.h +++ b/common/params_keys.h @@ -192,7 +192,7 @@ inline static std::unordered_map keys = { {"CEStatus", {CLEAR_ON_OFFROAD_TRANSITION, INT, "0", "0"}}, {"CEStopLights", {PERSISTENT, BOOL, "1", "0", 1}}, {"CEStoppedLead", {PERSISTENT, BOOL, "1", "0", 1}}, - {"ClusterOffset", {PERSISTENT, FLOAT, "1.015", "1.015", 2}}, + {"ClusterOffset", {PERSISTENT, FLOAT, "1.0", "1.0", 2}}, {"ColorScheme", {PERSISTENT, STRING, "frog", "stock", 0}}, {"ColorToDownload", {CLEAR_ON_MANAGER_START, STRING, "", ""}}, {"BootLogoToDownload", {CLEAR_ON_MANAGER_START, STRING, "", ""}}, diff --git a/common/params_pyx.so b/common/params_pyx.so index 74bc8c788..588d9535d 100755 Binary files a/common/params_pyx.so and b/common/params_pyx.so differ diff --git a/panda/board/obj/body_h7.bin.signed b/panda/board/obj/body_h7.bin.signed index b771f0a52..f8410cfb4 100644 Binary files a/panda/board/obj/body_h7.bin.signed and b/panda/board/obj/body_h7.bin.signed differ diff --git a/panda/board/obj/body_h7/bootstub.elf b/panda/board/obj/body_h7/bootstub.elf index 245c183b0..e71a7693e 100755 Binary files a/panda/board/obj/body_h7/bootstub.elf and b/panda/board/obj/body_h7/bootstub.elf differ diff --git a/panda/board/obj/body_h7/main.bin b/panda/board/obj/body_h7/main.bin index d7acf7979..c8e9db1cd 100755 Binary files a/panda/board/obj/body_h7/main.bin and b/panda/board/obj/body_h7/main.bin differ diff --git a/panda/board/obj/body_h7/main.elf b/panda/board/obj/body_h7/main.elf index 233c6bdac..10963798b 100755 Binary files a/panda/board/obj/body_h7/main.elf and b/panda/board/obj/body_h7/main.elf differ diff --git a/panda/board/obj/bootstub.body_h7.bin b/panda/board/obj/bootstub.body_h7.bin index 1f6eab49a..9d239622f 100755 Binary files a/panda/board/obj/bootstub.body_h7.bin and b/panda/board/obj/bootstub.body_h7.bin differ diff --git a/panda/board/obj/bootstub.panda.bin b/panda/board/obj/bootstub.panda.bin index e4b2f84c5..d7921bfd4 100755 Binary files a/panda/board/obj/bootstub.panda.bin and b/panda/board/obj/bootstub.panda.bin differ diff --git a/panda/board/obj/bootstub.panda_h7.bin b/panda/board/obj/bootstub.panda_h7.bin index 3d85c1809..4496c35fe 100755 Binary files a/panda/board/obj/bootstub.panda_h7.bin and b/panda/board/obj/bootstub.panda_h7.bin differ diff --git a/panda/board/obj/bootstub.panda_h7_remote.bin b/panda/board/obj/bootstub.panda_h7_remote.bin index 3d85c1809..4496c35fe 100755 Binary files a/panda/board/obj/bootstub.panda_h7_remote.bin and b/panda/board/obj/bootstub.panda_h7_remote.bin differ diff --git a/panda/board/obj/bootstub.panda_jungle_h7.bin b/panda/board/obj/bootstub.panda_jungle_h7.bin index b174aa147..68f72b9af 100755 Binary files a/panda/board/obj/bootstub.panda_jungle_h7.bin and b/panda/board/obj/bootstub.panda_jungle_h7.bin differ diff --git a/panda/board/obj/bootstub.panda_remote.bin b/panda/board/obj/bootstub.panda_remote.bin index e4b2f84c5..d7921bfd4 100755 Binary files a/panda/board/obj/bootstub.panda_remote.bin and b/panda/board/obj/bootstub.panda_remote.bin differ diff --git a/panda/board/obj/gitversion.h b/panda/board/obj/gitversion.h index b74793c88..fe719e47f 100644 --- a/panda/board/obj/gitversion.h +++ b/panda/board/obj/gitversion.h @@ -1,2 +1,2 @@ extern const uint8_t gitversion[19]; -const uint8_t gitversion[19] = "DEV-abd64084-DEBUG"; +const uint8_t gitversion[19] = "DEV-541c4d47-DEBUG"; diff --git a/panda/board/obj/panda.bin.signed b/panda/board/obj/panda.bin.signed index b261a9576..7f16977fd 100644 Binary files a/panda/board/obj/panda.bin.signed and b/panda/board/obj/panda.bin.signed differ diff --git a/panda/board/obj/panda/bootstub.elf b/panda/board/obj/panda/bootstub.elf index 02431f01c..c21e2c408 100755 Binary files a/panda/board/obj/panda/bootstub.elf and b/panda/board/obj/panda/bootstub.elf differ diff --git a/panda/board/obj/panda/main.bin b/panda/board/obj/panda/main.bin index dd03fa073..b6b7b9872 100755 Binary files a/panda/board/obj/panda/main.bin and b/panda/board/obj/panda/main.bin differ diff --git a/panda/board/obj/panda/main.elf b/panda/board/obj/panda/main.elf index 1e36bb872..f8b04e7e5 100755 Binary files a/panda/board/obj/panda/main.elf and b/panda/board/obj/panda/main.elf differ diff --git a/panda/board/obj/panda_h7.bin.signed b/panda/board/obj/panda_h7.bin.signed index e054a9352..1b4fca883 100644 Binary files a/panda/board/obj/panda_h7.bin.signed and b/panda/board/obj/panda_h7.bin.signed differ diff --git a/panda/board/obj/panda_h7/bootstub.elf b/panda/board/obj/panda_h7/bootstub.elf index 2e70c1804..c0afb7cf6 100755 Binary files a/panda/board/obj/panda_h7/bootstub.elf and b/panda/board/obj/panda_h7/bootstub.elf differ diff --git a/panda/board/obj/panda_h7/main.bin b/panda/board/obj/panda_h7/main.bin index 07f922806..410cf4927 100755 Binary files a/panda/board/obj/panda_h7/main.bin and b/panda/board/obj/panda_h7/main.bin differ diff --git a/panda/board/obj/panda_h7/main.elf b/panda/board/obj/panda_h7/main.elf index 6053eb4d1..f20b82021 100755 Binary files a/panda/board/obj/panda_h7/main.elf and b/panda/board/obj/panda_h7/main.elf differ diff --git a/panda/board/obj/panda_h7_remote.bin.signed b/panda/board/obj/panda_h7_remote.bin.signed index 47cd8c250..006e622f7 100644 Binary files a/panda/board/obj/panda_h7_remote.bin.signed and b/panda/board/obj/panda_h7_remote.bin.signed differ diff --git a/panda/board/obj/panda_h7_remote/bootstub.elf b/panda/board/obj/panda_h7_remote/bootstub.elf index f7871401c..a4fb1a36c 100755 Binary files a/panda/board/obj/panda_h7_remote/bootstub.elf and b/panda/board/obj/panda_h7_remote/bootstub.elf differ diff --git a/panda/board/obj/panda_h7_remote/main.bin b/panda/board/obj/panda_h7_remote/main.bin index 7a87a3082..25fe9f3dd 100755 Binary files a/panda/board/obj/panda_h7_remote/main.bin and b/panda/board/obj/panda_h7_remote/main.bin differ diff --git a/panda/board/obj/panda_h7_remote/main.elf b/panda/board/obj/panda_h7_remote/main.elf index 7dad688a7..7f0030c15 100755 Binary files a/panda/board/obj/panda_h7_remote/main.elf and b/panda/board/obj/panda_h7_remote/main.elf differ diff --git a/panda/board/obj/panda_jungle_h7.bin.signed b/panda/board/obj/panda_jungle_h7.bin.signed index 28794b02d..5fbae9e93 100644 Binary files a/panda/board/obj/panda_jungle_h7.bin.signed and b/panda/board/obj/panda_jungle_h7.bin.signed differ diff --git a/panda/board/obj/panda_jungle_h7/bootstub.elf b/panda/board/obj/panda_jungle_h7/bootstub.elf index 1969f0861..64bd05866 100755 Binary files a/panda/board/obj/panda_jungle_h7/bootstub.elf and b/panda/board/obj/panda_jungle_h7/bootstub.elf differ diff --git a/panda/board/obj/panda_jungle_h7/main.bin b/panda/board/obj/panda_jungle_h7/main.bin index 0c9f2a4f8..06598b21f 100755 Binary files a/panda/board/obj/panda_jungle_h7/main.bin and b/panda/board/obj/panda_jungle_h7/main.bin differ diff --git a/panda/board/obj/panda_jungle_h7/main.elf b/panda/board/obj/panda_jungle_h7/main.elf index b30a869fc..a57126fac 100755 Binary files a/panda/board/obj/panda_jungle_h7/main.elf and b/panda/board/obj/panda_jungle_h7/main.elf differ diff --git a/panda/board/obj/panda_remote.bin.signed b/panda/board/obj/panda_remote.bin.signed index 8d6e18dff..9c67c97cb 100644 Binary files a/panda/board/obj/panda_remote.bin.signed and b/panda/board/obj/panda_remote.bin.signed differ diff --git a/panda/board/obj/panda_remote/bootstub.elf b/panda/board/obj/panda_remote/bootstub.elf index 896e7967c..b06366432 100755 Binary files a/panda/board/obj/panda_remote/bootstub.elf and b/panda/board/obj/panda_remote/bootstub.elf differ diff --git a/panda/board/obj/panda_remote/main.bin b/panda/board/obj/panda_remote/main.bin index d5d6465a3..2fa9cffa2 100755 Binary files a/panda/board/obj/panda_remote/main.bin and b/panda/board/obj/panda_remote/main.bin differ diff --git a/panda/board/obj/panda_remote/main.elf b/panda/board/obj/panda_remote/main.elf index c349da935..a3dd832ee 100755 Binary files a/panda/board/obj/panda_remote/main.elf and b/panda/board/obj/panda_remote/main.elf differ diff --git a/panda/board/obj/version b/panda/board/obj/version index 0b02c6d9f..e953009ed 100644 --- a/panda/board/obj/version +++ b/panda/board/obj/version @@ -1 +1 @@ -DEV-abd64084-DEBUG \ No newline at end of file +DEV-541c4d47-DEBUG \ No newline at end of file diff --git a/selfdrive/pandad/pandad b/selfdrive/pandad/pandad index cf2b287f1..e28b810a0 100755 Binary files a/selfdrive/pandad/pandad and b/selfdrive/pandad/pandad differ diff --git a/selfdrive/ui/mici/onroad/cameraview.py b/selfdrive/ui/mici/onroad/cameraview.py index aac38ba71..0ca8e6b8b 100644 --- a/selfdrive/ui/mici/onroad/cameraview.py +++ b/selfdrive/ui/mici/onroad/cameraview.py @@ -37,63 +37,61 @@ void main() { } """ -# Choose fragment shader based on platform capabilities -if TICI: - FRAME_FRAGMENT_SHADER = """ - #version 300 es - #extension GL_OES_EGL_image_external_essl3 : enable - precision mediump float; - in vec2 fragTexCoord; - uniform samplerExternalOES texture0; - out vec4 fragColor; - uniform int engaged; - uniform int enhance_driver; +FRAME_FRAGMENT_SHADER_EXTERNAL = """ + #version 300 es + #extension GL_OES_EGL_image_external_essl3 : enable + precision mediump float; + in vec2 fragTexCoord; + uniform samplerExternalOES texture0; + out vec4 fragColor; + uniform int engaged; + uniform int enhance_driver; - void main() { - vec4 color = texture(texture0, fragTexCoord); - // Keep the onroad camera feed full-color in every driving state. - if (engaged == 1) { - color.rgb = color.rgb; - } - if (enhance_driver == 1) { - float brightness = 1.1; - color.rgb = color.rgb + 0.15; - color.rgb = clamp((color.rgb - 0.5) * (brightness * 0.8) + 0.5, 0.0, 1.0); - color.rgb = color.rgb * color.rgb * (3.0 - 2.0 * color.rgb); - color.rgb = pow(color.rgb, vec3(0.8)); - } - fragColor = vec4(color.rgb, color.a); + void main() { + vec4 color = texture(texture0, fragTexCoord); + // Keep the onroad camera feed full-color in every driving state. + if (engaged == 1) { + color.rgb = color.rgb; } - """ -else: - FRAME_FRAGMENT_SHADER = VERSION + """ - in vec2 fragTexCoord; - uniform sampler2D texture0; - uniform sampler2D texture1; - out vec4 fragColor; - uniform int engaged; - uniform int enhance_driver; + if (enhance_driver == 1) { + float brightness = 1.1; + color.rgb = color.rgb + 0.15; + color.rgb = clamp((color.rgb - 0.5) * (brightness * 0.8) + 0.5, 0.0, 1.0); + color.rgb = color.rgb * color.rgb * (3.0 - 2.0 * color.rgb); + color.rgb = pow(color.rgb, vec3(0.8)); + } + fragColor = vec4(color.rgb, color.a); + } + """ - void main() { - float y = texture(texture0, fragTexCoord).r; - vec2 uv = texture(texture1, fragTexCoord).ra - 0.5; - vec3 rgb = vec3(y + 1.402*uv.y, y - 0.344*uv.x - 0.714*uv.y, y + 1.772*uv.x); - // Keep the onroad camera feed full-color in every driving state. - if (engaged == 1) { - rgb = rgb; - } - // TODO: the images out of camerad need some more correction and - // the ui should apply a gamma curve for the device display - if (enhance_driver == 1) { - float brightness = 1.1; - rgb = rgb + 0.15; - rgb = clamp((rgb - 0.5) * (brightness * 0.8) + 0.5, 0.0, 1.0); - rgb = rgb * rgb * (3.0 - 2.0 * rgb); - rgb = pow(rgb, vec3(0.8)); - } - fragColor = vec4(rgb, 1.0); +FRAME_FRAGMENT_SHADER_YUV = VERSION + """ + in vec2 fragTexCoord; + uniform sampler2D texture0; + uniform sampler2D texture1; + out vec4 fragColor; + uniform int engaged; + uniform int enhance_driver; + + void main() { + float y = texture(texture0, fragTexCoord).r; + vec2 uv = texture(texture1, fragTexCoord).ra - 0.5; + vec3 rgb = vec3(y + 1.402*uv.y, y - 0.344*uv.x - 0.714*uv.y, y + 1.772*uv.x); + // Keep the onroad camera feed full-color in every driving state. + if (engaged == 1) { + rgb = rgb; } - """ + // TODO: the images out of camerad need some more correction and + // the ui should apply a gamma curve for the device display + if (enhance_driver == 1) { + float brightness = 1.1; + rgb = rgb + 0.15; + rgb = clamp((rgb - 0.5) * (brightness * 0.8) + 0.5, 0.0, 1.0); + rgb = rgb * rgb * (3.0 - 2.0 * rgb); + rgb = pow(rgb, vec3(0.8)); + } + fragColor = vec4(rgb, 1.0); + } + """ class CameraView(Widget): @@ -112,8 +110,13 @@ class CameraView(Widget): 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._use_egl = TICI and init_egl() + if TICI and not self._use_egl: + cloudlog.error("CameraView EGL init failed, falling back to texture rendering") + + frame_shader = FRAME_FRAGMENT_SHADER_EXTERNAL if self._use_egl else FRAME_FRAGMENT_SHADER_YUV + self.shader = rl.load_shader_from_memory(VERTEX_SHADER, frame_shader) + self._texture1_loc: int = rl.get_shader_location(self.shader, "texture1") if not self._use_egl 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") @@ -129,11 +132,8 @@ class CameraView(Widget): self._placeholder_color: rl.Color | None = None - # Initialize EGL for zero-copy rendering on TICI - if TICI: - if not init_egl(): - raise RuntimeError("Failed to initialize EGL") - + # Initialize EGL for zero-copy rendering when available. + if self._use_egl: # Create a 1x1 pixel placeholder texture for EGL image binding temp_image = rl.gen_image_color(1, 1, rl.BLACK) self.egl_texture = rl.load_texture_from_image(temp_image) @@ -181,7 +181,7 @@ class CameraView(Widget): self._clear_textures() # Clean up EGL texture - if TICI and self.egl_texture: + if self.egl_texture: rl.unload_texture(self.egl_texture) self.egl_texture = None @@ -256,7 +256,7 @@ class CameraView(Widget): dst_rect = rl.Rectangle(x_offset, y_offset, scale_x, scale_y) # Render with appropriate method - if TICI: + if self._use_egl: self._render_egl(src_rect, dst_rect) else: self._render_textures(src_rect, dst_rect) @@ -382,7 +382,7 @@ class CameraView(Widget): def _initialize_textures(self): self._clear_textures() - if not TICI: + if not self._use_egl: 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)) self.texture_uv = rl.load_texture_from_image(rl.Image(None, int(self.client.stride // 2), @@ -398,7 +398,7 @@ class CameraView(Widget): self.texture_uv = None # Clean up EGL resources - if TICI: + if self._use_egl: for data in self.egl_images.values(): destroy_egl_image(data) self.egl_images = {} diff --git a/selfdrive/ui/ui b/selfdrive/ui/ui index a82284754..98c46a226 100755 Binary files a/selfdrive/ui/ui and b/selfdrive/ui/ui differ diff --git a/selfdrive/ui/ui.py b/selfdrive/ui/ui.py index 99e482105..1251f45ad 100644 --- a/selfdrive/ui/ui.py +++ b/selfdrive/ui/ui.py @@ -3,7 +3,9 @@ import os from openpilot.system.hardware import TICI from openpilot.common.realtime import config_realtime_process, set_core_affinity +from openpilot.common.watchdog import kick_watchdog from openpilot.system.ui.lib.application import gui_app +from openpilot.selfdrive.ui.stall_monitor import UIStallMonitor from openpilot.selfdrive.ui.ui_state import ui_state BIG_UI = gui_app.big_ui() @@ -13,23 +15,43 @@ def main(): cores = {5, } config_realtime_process(0, 51) - gui_app.init_window("UI") - if BIG_UI: - from openpilot.selfdrive.ui.layouts.main import MainLayout - MainLayout() - else: - from openpilot.selfdrive.ui.mici.layouts.main import MiciMainLayout - MiciMainLayout() + stall_monitor = UIStallMonitor("raylib_ui") + stall_monitor.progress("ui.before_init_window") + stall_monitor.start() - for should_render in gui_app.render(): - ui_state.update() - if should_render: - # reaffine after power save offlines our core - if TICI and os.sched_getaffinity(0) != cores: - try: - set_core_affinity(list(cores)) - except OSError: - pass + try: + gui_app.init_window("UI") + stall_monitor.progress("ui.after_init_window") + gui_app.set_progress_hook(stall_monitor.progress) + kick_watchdog() + stall_monitor.progress("ui.before_layout_init") + if BIG_UI: + from openpilot.selfdrive.ui.layouts.main import MainLayout + MainLayout() + else: + from openpilot.selfdrive.ui.mici.layouts.main import MiciMainLayout + MiciMainLayout() + stall_monitor.progress("ui.after_layout_init") + kick_watchdog() + stall_monitor.progress("ui.loop_ready") + + for should_render in gui_app.render(): + stall_monitor.progress("ui.loop_iteration") + kick_watchdog() + stall_monitor.progress("ui.after_watchdog") + ui_state.update() + stall_monitor.progress("ui.after_state_update") + if should_render: + # reaffine after power save offlines our core + if TICI and os.sched_getaffinity(0) != cores: + try: + set_core_affinity(list(cores)) + except OSError: + pass + stall_monitor.progress("ui.loop_idle") + finally: + gui_app.set_progress_hook(None) + stall_monitor.stop() if __name__ == "__main__": diff --git a/system/camerad/camerad b/system/camerad/camerad index 1f9e195a7..8901b2543 100755 Binary files a/system/camerad/camerad and b/system/camerad/camerad differ diff --git a/system/loggerd/bootlog b/system/loggerd/bootlog index 19767b66d..efea266f2 100755 Binary files a/system/loggerd/bootlog and b/system/loggerd/bootlog differ diff --git a/system/loggerd/encoderd b/system/loggerd/encoderd index 5a55e2175..4a75228e4 100755 Binary files a/system/loggerd/encoderd and b/system/loggerd/encoderd differ diff --git a/system/loggerd/loggerd b/system/loggerd/loggerd index b8f763c5a..610bb89e7 100755 Binary files a/system/loggerd/loggerd and b/system/loggerd/loggerd differ diff --git a/system/manager/manager.py b/system/manager/manager.py index e40015343..5d0f4f98f 100755 --- a/system/manager/manager.py +++ b/system/manager/manager.py @@ -35,6 +35,7 @@ from openpilot.starpilot.common.starpilot_variables import ( LEGACY_BOLT_FP_MIGRATION_FLAG = Path("/data") / "legacy_bolt_fp_migration_v1" STARPILOT_DEFAULTS_PARITY_MIGRATION_FLAG = Path("/data") / "starpilot_defaults_parity_v1" STARPILOT_HUMANLIKE_DISABLE_MIGRATION_FLAG = Path("/data") / "starpilot_humanlike_disable_v1" +STARPILOT_CLUSTER_OFFSET_MIGRATION_FLAG = Path("/data") / "starpilot_cluster_offset_v1" STARPILOT_PARAM_RENAME_MIGRATION_FLAG = Path("/data") / "starpilot_param_rename_v1" STARPILOT_PARAM_CANONICALIZATION_MIGRATION_FLAG = Path("/data") / "starpilot_param_canonicalization_v1" STARPILOT_PC_ROOT_MIGRATION_FLAG = Path("/data") / "starpilot_pc_root_v1" @@ -401,6 +402,37 @@ def migrate_disable_humanlike_defaults(params: Params, params_cache: Params) -> cloudlog.exception(f"Failed to write migration flag: {STARPILOT_HUMANLIKE_DISABLE_MIGRATION_FLAG}") +def migrate_cluster_offset_default(params: Params, params_cache: Params) -> None: + if STARPILOT_CLUSTER_OFFSET_MIGRATION_FLAG.exists(): + return + + legacy_default_detected = False + for params_obj in (params, params_cache): + raw_value = _read_raw_param_bytes(params_obj, "ClusterOffset") + if not raw_value: + continue + + try: + parsed_value = float(raw_value.decode("utf-8", errors="strict").strip()) + except Exception: + continue + + if abs(parsed_value - 1.015) < 1e-6: + legacy_default_detected = True + break + + if legacy_default_detected: + params.put_float("ClusterOffset", 1.0) + params_cache.put_float("ClusterOffset", 1.0) + cloudlog.warning("Applied one-time ClusterOffset migration from 1.015 to 1.0") + + try: + STARPILOT_CLUSTER_OFFSET_MIGRATION_FLAG.parent.mkdir(parents=True, exist_ok=True) + STARPILOT_CLUSTER_OFFSET_MIGRATION_FLAG.write_text(f"{datetime.datetime.now(datetime.UTC).isoformat()}\n") + except Exception: + cloudlog.exception(f"Failed to write migration flag: {STARPILOT_CLUSTER_OFFSET_MIGRATION_FLAG}") + + def _read_raw_param_bytes(params: Params, key: str | bytes): try: path = params.get_param_path(key) @@ -568,6 +600,7 @@ def manager_init() -> None: migrate_param_type_canonicalization(params) migrate_starpilot_default_parity(params, params_cache) migrate_disable_humanlike_defaults(params, params_cache) + migrate_cluster_offset_default(params, params_cache) # set unset params to their default value for k in params.all_keys(): diff --git a/system/manager/test/test_manager.py b/system/manager/test/test_manager.py index 8f2224c20..dc199315e 100644 --- a/system/manager/test/test_manager.py +++ b/system/manager/test/test_manager.py @@ -191,6 +191,32 @@ class TestManager: assert not params_cache.get_bool("HumanFollowing") assert not params_cache.get_bool("HumanLaneChanges") + def test_migrate_cluster_offset_default_resets_legacy_default_only(self, tmp_path, monkeypatch): + monkeypatch.setattr(manager, "STARPILOT_CLUSTER_OFFSET_MIGRATION_FLAG", tmp_path / "starpilot_cluster_offset_v1") + + params = FileBackedFakeParams(tmp_path / "params", { + "ClusterOffset": 1.015, + }) + params_cache = FileBackedFakeParams(tmp_path / "cache", {}) + + manager.migrate_cluster_offset_default(params, params_cache) + + assert params.get("ClusterOffset") == "1.0" + assert params_cache.get("ClusterOffset") == "1.0" + + def test_migrate_cluster_offset_default_preserves_custom_values(self, tmp_path, monkeypatch): + monkeypatch.setattr(manager, "STARPILOT_CLUSTER_OFFSET_MIGRATION_FLAG", tmp_path / "starpilot_cluster_offset_v1") + + params = FileBackedFakeParams(tmp_path / "params", { + "ClusterOffset": 1.02, + }) + params_cache = FileBackedFakeParams(tmp_path / "cache", {}) + + manager.migrate_cluster_offset_default(params, params_cache) + + assert params.get("ClusterOffset") == "1.02" + assert params_cache.get("ClusterOffset") is None + @pytest.mark.skip("this test is flaky the way it's currently written, should be moved to test_onroad") def test_clean_exit(self, subtests): """ diff --git a/system/ui/lib/application.py b/system/ui/lib/application.py index 980410b02..8c2ec102b 100644 --- a/system/ui/lib/application.py +++ b/system/ui/lib/application.py @@ -218,6 +218,7 @@ class GuiApplication: self._ffmpeg_queue: queue.Queue | None = None self._ffmpeg_thread: threading.Thread | None = None self._ffmpeg_stop_event: threading.Event | None = None + self._progress_hook: Callable[[str], None] | None = None self._textures: dict[str, rl.Texture] = {} self._target_fps: int = _DEFAULT_FPS self._last_fps_log_time: float = time.monotonic() @@ -447,6 +448,13 @@ class GuiApplication: if tick_function in self._nav_stack_ticks: self._nav_stack_ticks.remove(tick_function) + def set_progress_hook(self, hook: Callable[[str], None] | None) -> None: + self._progress_hook = hook + + def _mark_progress(self, phase: str) -> None: + if self._progress_hook is not None: + self._progress_hook(phase) + def set_should_render(self, should_render: bool): self._should_render = should_render @@ -585,6 +593,7 @@ class GuiApplication: self._render_profiler.enable() while not (self._window_close_requested or rl.window_should_close()): + self._mark_progress("gui_app.loop_start") if PC: # Thread is not used on PC, need to manually add mouse events self._mouse._handle_mouse_event() @@ -596,6 +605,7 @@ class GuiApplication: # Skip rendering when screen is off if not self._should_render: + self._mark_progress("gui_app.skip_render") if PC: rl.poll_input_events() time.sleep(1 / self._target_fps) @@ -603,43 +613,63 @@ class GuiApplication: continue if self._render_texture: + self._mark_progress("gui_app.before_begin_texture_mode") rl.begin_texture_mode(self._render_texture) + self._mark_progress("gui_app.after_begin_texture_mode") + self._mark_progress("gui_app.before_clear_background") rl.clear_background(rl.BLACK) + self._mark_progress("gui_app.after_clear_background") else: + self._mark_progress("gui_app.before_begin_drawing") rl.begin_drawing() + self._mark_progress("gui_app.after_begin_drawing") + self._mark_progress("gui_app.before_clear_background") rl.clear_background(rl.BLACK) + self._mark_progress("gui_app.after_clear_background") if self._scale != 1.0: rl.rl_push_matrix() rl.rl_scalef(self._scale, self._scale, 1.0) # Allow a Widget to still run a function regardless of the stack depth + self._mark_progress("gui_app.before_nav_ticks") for tick in self._nav_stack_ticks: tick() + self._mark_progress("gui_app.after_nav_ticks") # Only render top widgets + self._mark_progress("gui_app.before_widget_render") for widget in self._nav_stack[-self._nav_stack_widgets_to_render:]: widget.render(rl.Rectangle(0, 0, self.width, self.height)) + self._mark_progress("gui_app.after_widget_render") + self._mark_progress("gui_app.frame_ready") yield True if self._scale != 1.0: rl.rl_pop_matrix() if self._render_texture: + self._mark_progress("gui_app.end_texture_mode") rl.end_texture_mode() + self._mark_progress("gui_app.before_present_begin_drawing") rl.begin_drawing() + self._mark_progress("gui_app.after_present_begin_drawing") + self._mark_progress("gui_app.before_present_clear_background") rl.clear_background(rl.BLACK) + self._mark_progress("gui_app.after_present_clear_background") src_rect = rl.Rectangle(0, 0, float(self._scaled_width), -float(self._scaled_height)) dst_rect = rl.Rectangle(0, 0, float(self._scaled_width), float(self._scaled_height)) texture = self._render_texture.texture if texture: + self._mark_progress("gui_app.before_present_draw_texture") if BURN_IN_MODE and self._burn_in_shader: rl.begin_shader_mode(self._burn_in_shader) rl.draw_texture_pro(texture, src_rect, dst_rect, rl.Vector2(0, 0), 0.0, rl.WHITE) rl.end_shader_mode() else: rl.draw_texture_pro(texture, src_rect, dst_rect, rl.Vector2(0, 0), 0.0, rl.WHITE) + self._mark_progress("gui_app.after_present_draw_texture") if self._show_fps: rl.draw_fps(10, 10) @@ -650,7 +680,9 @@ class GuiApplication: if self._grid_size > 0: self._draw_grid() + self._mark_progress("gui_app.before_end_drawing") rl.end_drawing() + self._mark_progress("gui_app.after_end_drawing") if RECORD: image = rl.load_image_from_texture(self._render_texture.texture) @@ -661,6 +693,7 @@ class GuiApplication: self._monitor_fps() self._frame += 1 + self._mark_progress("gui_app.loop_idle") if self._profile_render_frames > 0 and self._frame >= self._profile_render_frames: self._output_render_profile()