diff --git a/modules/ui_shared/src/ui/startup_shell.cpp b/modules/ui_shared/src/ui/startup_shell.cpp index 50926ebe..580688f6 100644 --- a/modules/ui_shared/src/ui/startup_shell.cpp +++ b/modules/ui_shared/src/ui/startup_shell.cpp @@ -1,10 +1,13 @@ #include "ui/startup_shell.h" +#include +#include #include #include "lvgl.h" #include "platform/ui/screen_runtime.h" #include "platform/ui/time_runtime.h" +#include "sys/clock.h" #include "ui/app_runtime.h" #include "ui/localization.h" #include "ui/menu/menu_layout.h" @@ -23,6 +26,9 @@ namespace ui::menu::MenuModel s_ux_menu_model; +constexpr uint8_t kBootPresentFrameCount = 4; +constexpr uint32_t kBootPresentFrameDelayMs = 16; + bool resolve_display_time(struct tm* out_tm) { if (!out_tm) @@ -54,11 +60,19 @@ bool resolve_display_time(struct tm* out_tm) void present_boot_overlay_now() { - if (lv_obj_t* top = lv_layer_top()) + for (uint8_t frame = 0; frame < kBootPresentFrameCount; ++frame) { - lv_obj_invalidate(top); + if (lv_obj_t* top = lv_layer_top()) + { + lv_obj_invalidate(top); + } + lv_timer_handler(); + lv_refr_now(nullptr); + if (frame + 1U < kBootPresentFrameCount) + { + sys::sleep_ms(kBootPresentFrameDelayMs); + } } - lv_timer_handler(); } } // namespace @@ -140,12 +154,18 @@ platform::ui::screen::Hooks buildScreenSleepHooks(const Hooks& hooks) void setBootLogLine(const char* line) { + std::printf("[BOOT][UI] log line=%s\n", line ? line : ""); + std::fflush(stdout); ui::boot::set_log_line(line); present_boot_overlay_now(); } void beginBootUi(bool waking_from_sleep, const char* initial_line) { + std::printf("[BOOT][UI] show waking=%d line=%s\n", + waking_from_sleep ? 1 : 0, + initial_line ? initial_line : ""); + std::fflush(stdout); if (!waking_from_sleep) { ui::boot::show(); @@ -188,6 +208,8 @@ void initializeShell(const Hooks& hooks) void finalizeStartup(bool waking_from_sleep) { + std::printf("[BOOT][UI] ready waking=%d\n", waking_from_sleep ? 1 : 0); + std::fflush(stdout); if (waking_from_sleep) { platform::ui::screen::update_user_activity(); diff --git a/modules/ui_shared/src/ui/startup_ui_shell.cpp b/modules/ui_shared/src/ui/startup_ui_shell.cpp index df85b5af..e2cdd61e 100644 --- a/modules/ui_shared/src/ui/startup_ui_shell.cpp +++ b/modules/ui_shared/src/ui/startup_ui_shell.cpp @@ -1,9 +1,12 @@ #include "ui/startup_ui_shell.h" +#include +#include #include #include "lvgl.h" #include "platform/ui/time_runtime.h" +#include "sys/clock.h" #include "ui/app_runtime.h" #include "ui/localization.h" #include "ui/menu/menu_layout.h" @@ -20,13 +23,24 @@ namespace bool s_shell_initialized = false; ui::menu::MenuModel s_ux_menu_model; +constexpr uint8_t kBootPresentFrameCount = 4; +constexpr uint32_t kBootPresentFrameDelayMs = 16; + void present_boot_overlay_now() { - if (lv_obj_t* top = lv_layer_top()) + for (uint8_t frame = 0; frame < kBootPresentFrameCount; ++frame) { - lv_obj_invalidate(top); + if (lv_obj_t* top = lv_layer_top()) + { + lv_obj_invalidate(top); + } + lv_timer_handler(); + lv_refr_now(nullptr); + if (frame + 1U < kBootPresentFrameCount) + { + sys::sleep_ms(kBootPresentFrameDelayMs); + } } - lv_timer_handler(); } bool resolve_display_time(struct tm* out_tm) @@ -101,6 +115,10 @@ void unlock_ui(const Hooks& hooks) bool beginBootUi(const Hooks& hooks, bool waking_from_sleep, const char* initial_line) { + std::printf("[BOOT][UI] show waking=%d line=%s\n", + waking_from_sleep ? 1 : 0, + initial_line ? initial_line : ""); + std::fflush(stdout); if (waking_from_sleep) { return true; @@ -110,6 +128,8 @@ bool beginBootUi(const Hooks& hooks, bool waking_from_sleep, const char* initial return false; } ui::boot::show(); + std::printf("[BOOT][UI] log line=%s\n", initial_line ? initial_line : ""); + std::fflush(stdout); ui::boot::set_log_line(initial_line); present_boot_overlay_now(); unlock_ui(hooks); @@ -155,7 +175,8 @@ bool initializeMenuSkeleton(const Hooks& hooks) bool finalizeStartup(const Hooks& hooks, bool waking_from_sleep) { - (void)waking_from_sleep; + std::printf("[BOOT][UI] ready waking=%d\n", waking_from_sleep ? 1 : 0); + std::fflush(stdout); if (!lock_ui(hooks)) { return false; diff --git a/platform/esp/arduino_common/src/ui/widgets/map/map_tiles.cpp b/platform/esp/arduino_common/src/ui/widgets/map/map_tiles.cpp index fe8062b0..35f968a7 100644 --- a/platform/esp/arduino_common/src/ui/widgets/map/map_tiles.cpp +++ b/platform/esp/arduino_common/src/ui/widgets/map/map_tiles.cpp @@ -61,6 +61,8 @@ static uint32_t g_cache_full_log_ms = 0; static uint8_t g_requested_map_source = 0; +constexpr std::size_t kMapTileSdReadChunkBytes = 2U * 1024U; + static void style_placeholder_card(lv_obj_t* card); static void style_placeholder_text(lv_obj_t* label); @@ -141,6 +143,10 @@ class PathOnlyMapTileFileSystem final : public ui::map_tiles::IMapTileFileSystem }; #if defined(ARDUINO) || defined(ARDUINO_ARCH_ESP32) +bool yield_map_tile_sd_bus_between_chunks(const char* path, + std::size_t bytes_read, + std::size_t total_bytes); + class SdMapTileFileSystem final : public ui::map_tiles::IMapTileFileSystem { public: @@ -178,15 +184,32 @@ class SdMapTileFileSystem final : public ui::map_tiles::IMapTileFileSystem return false; } - const int bytes_read = file.read(buffer, static_cast(file_size)); - file.close(); - if (bytes_read < 0 || static_cast(bytes_read) != file_size) + const std::size_t target_size = static_cast(file_size); + std::size_t total_read = 0; + while (total_read < target_size) { - out_size = bytes_read > 0 ? static_cast(bytes_read) : 0; - return false; + const std::size_t chunk_size = + std::min(kMapTileSdReadChunkBytes, target_size - total_read); + const int bytes_read = file.read(buffer + total_read, chunk_size); + if (bytes_read <= 0) + { + out_size = total_read; + file.close(); + return false; + } + + total_read += static_cast(bytes_read); + if (total_read < target_size && + !yield_map_tile_sd_bus_between_chunks(path, total_read, target_size)) + { + out_size = total_read; + file.close(); + return false; + } } - out_size = static_cast(bytes_read); + file.close(); + out_size = total_read; return true; } }; @@ -198,13 +221,15 @@ constexpr std::size_t kMapTileWorkerScratchBytes = 192U * 1024U; constexpr int kMapTileEventsPerUiDrain = 1; constexpr uint32_t kMapTileUiDrainBudgetMs = 4; constexpr uint32_t kMapTileUiEventCooldownMs = 120; -constexpr uint32_t kMapTileLayerBusyBackoffMs = 1500; +constexpr uint32_t kMapTileLayerBusyBackoffMs = 5000; constexpr uint32_t kMapTileLayerCacheBackoffMs = 350; constexpr uint32_t kMapTileMissingCacheTtlMs = 5U * 60U * 1000U; constexpr uint32_t kMapTileGenerationInitial = 1; constexpr uint32_t kMapTileBusResource = 1; constexpr uint32_t kMapTileDiagnosticLogIntervalMs = 1000; constexpr TickType_t kMapTileWorkerPostCommandYieldTicks = pdMS_TO_TICKS(32); +constexpr TickType_t kMapTileSdChunkYieldTicks = pdMS_TO_TICKS(1); +constexpr TickType_t kMapTileSdChunkReacquireTicks = pdMS_TO_TICKS(50); constexpr uint32_t kMapTileDisplaySpiSlowHoldMs = 8; constexpr uint32_t kMapTileDisplaySpiNormalCooldownMs = 160; constexpr uint32_t kMapTileDisplaySpiSlowCooldownMs = 1500; @@ -213,6 +238,7 @@ constexpr uint32_t kMapTileDisplayPressureCooldownMs = 1500; uint32_t g_map_tile_decode_log_ms = 0; uint32_t g_map_tile_event_log_ms = 0; uint32_t g_map_tile_next_event_drain_ms = 0; +uint32_t g_map_tile_chunk_yield_log_ms = 0; bool should_log_map_tile_diagnostic(uint32_t& last_ms, uint32_t now_ms) { @@ -224,6 +250,35 @@ bool should_log_map_tile_diagnostic(uint32_t& last_ms, uint32_t now_ms) return false; } +bool yield_map_tile_sd_bus_between_chunks(const char* path, + std::size_t bytes_read, + std::size_t total_bytes) +{ + ::platform::esp::common::shared_spi_unlock(); + vTaskDelay(kMapTileSdChunkYieldTicks); + + if (::platform::esp::common::shared_spi_lock(kMapTileSdChunkReacquireTicks)) + { + return true; + } + + const uint32_t now_ms = sys::millis_now(); + if (should_log_map_tile_diagnostic(g_map_tile_chunk_yield_log_ms, now_ms)) + { + std::printf("[GPS][MAP][bus] chunk_reacquire_timeout path=%s bytes=%u/%u\n", + path ? path : "", + static_cast(bytes_read), + static_cast(total_bytes)); + std::fflush(stdout); + } + + while (!::platform::esp::common::shared_spi_lock(pdMS_TO_TICKS(100))) + { + vTaskDelay(kMapTileSdChunkYieldTicks); + } + return false; +} + const char* map_tile_format_name(ui::map_tiles::MapTileFormat format) { switch (format) @@ -915,13 +970,17 @@ class EspMapTileBusArbiter final : public sys::runtime::IBusArbiter const uint32_t release_ms = sys::millis_now(); const uint32_t hold_ms = release_ms - token.acquired_ms; ::platform::esp::common::shared_spi_unlock(); - const bool slow_hold = hold_ms >= kMapTileDisplaySpiSlowHoldMs; + const bool display_pressure = + ::platform::esp::common::display_spi_recently_timed_out( + release_ms, + kMapTileDisplayPressureCooldownMs); + const bool slow_hold = display_pressure && hold_ms >= kMapTileDisplaySpiSlowHoldMs; const uint32_t cooldown_ms = slow_hold ? kMapTileDisplaySpiSlowCooldownMs : kMapTileDisplaySpiNormalCooldownMs; cooldown_until_ms_ = release_ms + cooldown_ms; if (slow_hold && should_log_map_tile_diagnostic(last_slow_hold_log_ms_, release_ms)) { - std::printf("[GPS][MAP][bus] display_shared_slow_hold hold_ms=%lu cooldown_ms=%lu\n", + std::printf("[GPS][MAP][bus] display_shared_slow_hold hold_ms=%lu cooldown_ms=%lu display_pressure=1\n", static_cast(hold_ms), static_cast(cooldown_ms)); std::fflush(stdout);