From 13e87ff4703209e16561108ef27b4326f5c0aa86 Mon Sep 17 00:00:00 2001 From: jpirnay Date: Fri, 8 May 2026 17:29:59 +0200 Subject: [PATCH] Reduce memory need --- src/activities/home/HomeActivity.cpp | 10 ++++++++ src/activities/reader/ReaderActivity.cpp | 7 +++++- src/components/themes/BaseTheme.h | 4 ++++ .../themes/lyra/LyraCarouselTheme.cpp | 24 ++++++++++--------- .../themes/lyra/LyraCarouselTheme.h | 2 +- 5 files changed, 34 insertions(+), 13 deletions(-) diff --git a/src/activities/home/HomeActivity.cpp b/src/activities/home/HomeActivity.cpp index 64ea36ad..04830178 100644 --- a/src/activities/home/HomeActivity.cpp +++ b/src/activities/home/HomeActivity.cpp @@ -182,7 +182,10 @@ void HomeActivity::loadRecentBooks(int maxBooks) { if (!sidecar.empty()) { const bool sidecarAlreadyStored = book.coverBmpPath == sidecar || book.coverBmpPath.find("sidecar_") != std::string::npos; + LOG_DBG("HOME", "Sidecar for %s: stored=%s alreadyStored=%d", book.path.c_str(), book.coverBmpPath.c_str(), + sidecarAlreadyStored ? 1 : 0); if (!sidecarAlreadyStored) { + LOG_DBG("HOME", "Updating coverBmpPath to sidecar: %s", sidecar.c_str()); RECENT_BOOKS.updateBook(book.path, book.title, book.author, book.series, sidecar); RecentBook updated = book; updated.coverBmpPath = sidecar; @@ -208,7 +211,9 @@ void HomeActivity::loadRecentCovers(int coverHeight) { const bool isSidecar = FsHelpers::hasJpgExtension(book.coverBmpPath) || FsHelpers::hasPngExtension(book.coverBmpPath); if (isSidecar) { + LOG_DBG("HOME", "Converting sidecar %s for book %s", book.coverBmpPath.c_str(), book.path.c_str()); if (!Storage.exists(book.coverBmpPath.c_str())) { + LOG_ERR("HOME", "Sidecar file missing: %s", book.coverBmpPath.c_str()); RECENT_BOOKS.updateBook(book.path, book.title, book.author, book.series, ""); book.coverBmpPath = ""; } else { @@ -229,6 +234,7 @@ void HomeActivity::loadRecentCovers(int coverHeight) { if (convertSidecarToBmp(book.path, book.coverBmpPath, w, coverHeight, name).empty()) success = false; } if (success) { + LOG_DBG("HOME", "Sidecar converted, placeholder: %s", placeholder.c_str()); RECENT_BOOKS.updateBook(book.path, book.title, book.author, book.series, placeholder); book.coverBmpPath = placeholder; } else { @@ -374,6 +380,7 @@ void HomeActivity::onEnter() { void HomeActivity::onExit() { Activity::onExit(); freeCoverBuffer(); + UITheme::getInstance().getMutableTheme().invalidateFrameCache(); } bool HomeActivity::storeCoverBuffer() { @@ -564,6 +571,9 @@ void HomeActivity::render(RenderLock&&) { if (!firstRenderDone) { firstRenderDone = true; requestUpdate(); + } else if (!recentsLoaded && !recentsLoading) { + recentsLoading = true; + loadRecentCovers(getHomeCoverRenderHeight(computeHomeScreenLayout(metrics, contentRect.height, menuCount))); } } diff --git a/src/activities/reader/ReaderActivity.cpp b/src/activities/reader/ReaderActivity.cpp index 34ee7d17..a9fa44be 100644 --- a/src/activities/reader/ReaderActivity.cpp +++ b/src/activities/reader/ReaderActivity.cpp @@ -62,8 +62,13 @@ std::string ReaderActivity::sidecarCoverPath(const std::string& bookPath) { const std::string base = bookPath.substr(0, dot); for (const char* ext : {".jpg", ".jpeg", ".png", ".bmp"}) { const std::string candidate = base + ext; - if (Storage.exists(candidate.c_str())) return candidate; + LOG_DBG("SIDECAR", "Checking: %s", candidate.c_str()); + if (Storage.exists(candidate.c_str())) { + LOG_DBG("SIDECAR", "Found sidecar cover: %s", candidate.c_str()); + return candidate; + } } + LOG_DBG("SIDECAR", "No sidecar found for: %s", bookPath.c_str()); return ""; } diff --git a/src/components/themes/BaseTheme.h b/src/components/themes/BaseTheme.h index f42f9b3f..76415592 100644 --- a/src/components/themes/BaseTheme.h +++ b/src/components/themes/BaseTheme.h @@ -193,6 +193,10 @@ class BaseTheme { // Only one of epub/xtc/txt will be non-null depending on the reader. virtual void onBookWillClose(const std::string& path, Epub* epub, Xtc* xtc, Txt* txt) {} + // Called when HomeActivity exits. Themes that hold heap-allocated render caches + // should free them here so the memory is available to child activities. + virtual void invalidateFrameCache() {} + // ---- Shared constants and helpers for battery drawing (used by all themes) ---- static constexpr int batteryPercentSpacing = 4; static void drawBatteryOutline(const GfxRenderer& renderer, int x, int y, int battWidth, int rectHeight); diff --git a/src/components/themes/lyra/LyraCarouselTheme.cpp b/src/components/themes/lyra/LyraCarouselTheme.cpp index e6a219b7..da850aa3 100644 --- a/src/components/themes/lyra/LyraCarouselTheme.cpp +++ b/src/components/themes/lyra/LyraCarouselTheme.cpp @@ -90,9 +90,9 @@ const uint8_t* iconBitmapFor(UIIcon icon) { // home after settings doesn't re-read covers from SD. // Freed explicitly via invalidateFrameCache() before entering the reader. // --------------------------------------------------------------------------- -constexpr int kFrameCount = 3; +constexpr int kFrameCount = 1; uint8_t* gCachedFrames[kFrameCount] = {}; -int gCachedFrameBookIdx[kFrameCount] = {-1, -1, -1}; +int gCachedFrameBookIdx[kFrameCount] = {-1}; int gCachedFrameCount = 0; std::string gCacheKey; @@ -102,14 +102,8 @@ int findFrameSlot(int bookIdx) { } return -1; } -} // namespace -// --------------------------------------------------------------------------- -// Static helpers -// --------------------------------------------------------------------------- -void LyraCarouselTheme::setPreRenderIndex(int idx) { lastCarouselSelectorIndex = idx; } - -void LyraCarouselTheme::invalidateFrameCache() const { +void freeFrameCache() { for (int i = 0; i < kFrameCount; ++i) { if (gCachedFrames[i]) { free(gCachedFrames[i]); @@ -120,6 +114,14 @@ void LyraCarouselTheme::invalidateFrameCache() const { gCachedFrameCount = 0; gCacheKey.clear(); } +} // namespace + +// --------------------------------------------------------------------------- +// Static helpers +// --------------------------------------------------------------------------- +void LyraCarouselTheme::setPreRenderIndex(int idx) { lastCarouselSelectorIndex = idx; } + +void LyraCarouselTheme::invalidateFrameCache() { freeFrameCache(); } void LyraCarouselTheme::onBookWillClose(const std::string& /*path*/, Epub* epub, Xtc* xtc, Txt* /*txt*/) { if (epub) { @@ -212,13 +214,13 @@ bool LyraCarouselTheme::tryFastHomeRender(GfxRenderer& renderer, const std::vect if (!framesReady) { // Free old cache and allocate fresh frames - invalidateFrameCache(); + freeFrameCache(); const int frameCount = std::min(bookCount, kFrameCount); for (int i = 0; i < frameCount; ++i) { gCachedFrames[i] = static_cast(malloc(bufferSize)); if (!gCachedFrames[i]) { LOG_ERR("CAROUSEL", "tryFastHomeRender: malloc failed for frame %d", i); - invalidateFrameCache(); + freeFrameCache(); return false; } } diff --git a/src/components/themes/lyra/LyraCarouselTheme.h b/src/components/themes/lyra/LyraCarouselTheme.h index a848d949..63019665 100644 --- a/src/components/themes/lyra/LyraCarouselTheme.h +++ b/src/components/themes/lyra/LyraCarouselTheme.h @@ -72,7 +72,7 @@ class LyraCarouselTheme : public LyraTheme { const std::function& menuIcon, const char* hintBtn1, const char* hintBtn2, const char* hintBtn3, const char* hintBtn4) const override; void onBookWillClose(const std::string& path, Epub* epub, Xtc* xtc, Txt* txt) override; - void invalidateFrameCache() const; + void invalidateFrameCache() override; void drawRecentBookCover(GfxRenderer& renderer, Rect rect, const std::vector& recentBooks, const int selectorIndex, bool& coverRendered, bool& coverBufferStored, bool& bufferRestored,