Skip to content

perf(fonts): batch-prewarm CJK library navigation - #3

Merged
szetszho merged 3 commits into
szetszho:developfrom
CollieIsCute:develop
Jul 22, 2026
Merged

perf(fonts): batch-prewarm CJK library navigation#3
szetszho merged 3 commits into
szetszho:developfrom
CollieIsCute:develop

Conversation

@CollieIsCute

@CollieIsCute CollieIsCute commented Jul 11, 2026

Copy link
Copy Markdown

Hi @szetszho,

This improves Up/Down navigation in directories with many Chinese books by about 5x on my Xteink X4: full-screen rendering dropped from roughly 5 seconds to 1 second.

This PR keeps:

  • batch-prewarming visible list text;
  • expanding the UI glyph cache from 128 to 256.

After testing the LRU implementation on-device, I found no user-visible speedup in this workflow. This PR therefore removes all LRU-related changes and retains the smaller fixed-cache implementation.

I will revisit an LRU design later if it can materially reduce the remaining latency. The current roughly one-second response is usable, and this branch tested normally on my X4.

@CollieIsCute

CollieIsCute commented Jul 11, 2026

Copy link
Copy Markdown
Author

@szetszho 您好,我測試了一陣子此 PR 之後的心得是:「五秒改善為一秒可接受,但仍想更進一步加速」。
我正嘗試把 lru cache 機制加入 prewarm 流程中,讓整體體驗更好,實測後如果有提昇速度會再更新此 PR.

@CollieIsCute
CollieIsCute marked this pull request as draft July 11, 2026 12:04
@CollieIsCute CollieIsCute changed the title perf(fonts): speed up CJK library navigation perf(fonts): batch-prewarm CJK library navigation Jul 12, 2026
@CollieIsCute

Copy link
Copy Markdown
Author

@szetszho 實際測試過,發現 lru cache 機制讓 ui 使用的延遲維持依然在 1 秒左右。
因此我刪去 lru 相關變更,只恢復原本精簡的 cache 機制。

@CollieIsCute
CollieIsCute marked this pull request as ready for review July 12, 2026 19:14
@szetszho

Copy link
Copy Markdown
Owner

@szetszho 實際測試過,發現 lru cache 機制讓 ui 使用的延遲維持依然在 1 秒左右。 因此我刪去 lru 相關變更,只恢復原本精簡的 cache 機制。

你有在用哪個字體?

Copy link
Copy Markdown
Owner

Thanks for this — the approach is solid (batch-before-loop, CacheOwner tracking, transition-driven eviction), and I verified the prewarm font/style pairs match the actual draw calls in all three themes. A few things before merging:

1. Fast-path early-out in prewarmUi() (perf/heap churn)
Every call allocates a 1KB codepoint buffer, runs the retention scan + sort, and only then hits the needsRebuild early return. Since resolveTextFontId fires on truncatedText, getTextWidth, and drawText, a 10-row CJK list pays this ~20–30× per render in the steady state. Suggest: when cacheOwner_ == Ui, first check just the incoming text's codepoints against epdFont.hasCodepoint() and return immediately if all present — this also removes the repeated 1KB alloc/free per draw call.

2. Shared font instance when reader size == UI size (needs on-device test)
loadFamilyExtraSize() reuses the reader-size SdCardFont when it matches a UI size (e.g. both 12pt). In that config cacheOwner_ ping-pongs within a single reader page render: page prewarm() sets ReaderPage, then CJK UI chrome (status bar with a Chinese title) redirects → prewarmUi()clearCache() wipes the just-warmed page glyphs. If that chrome draws before the page body, every page glyph becomes a per-glyph SD miss. Please test with a font pack whose reader size collides with 8/10/12pt and a CJK title visible in the reader.

3. Heap numbers for the doubled cache
MAX_UI_GLYPHS 128→256 with union retention means up to ~25KB per UI fallback size at 12pt, and up to three sizes warm at once in UI mode. Could you share ESP.getFreeHeap() on a CJK-heavy library screen before/after?

Minor (fine as follow-ups):

  • resolveTextFontId() now mutates cache state but can be called from the main task (text measurement in onEnter()/loop()) concurrently with the render task — SdCardFont has no mutex, so please document the thread-confinement assumption at least.
  • Prefer makeUniqueNoThrow<uint32_t[]>() (lib/Memory) over raw new (std::nothrow) per project convention.
  • clearUiCache() is identical to clearCache() and neither checks cacheOwner_ — a comment clarifying they're full clears would help.
  • syncFontCacheMode() in replaceActivity()'s no-current-activity branch runs without the RenderLock, unlike the other call sites (startup-only, but cheap to make consistent).

Generated by Claude Code

@CollieIsCute

CollieIsCute commented Jul 19, 2026

Copy link
Copy Markdown
Author

@szetszho 實際測試過,發現 lru cache 機制讓 ui 使用的延遲維持依然在 1 秒左右。 因此我刪去 lru 相關變更,只恢復原本精簡的 cache 機制。

你有在用哪個字體?

我一般都用 NotoSansMonoCJK 8~18 的字體。
NotoSansMonoCJK.zip

Skip codepoint buffer allocation when requested UI glyphs are already cached. Serialize cache mode changes and main-task text measurement with RenderLock.
@szetszho
szetszho merged this pull request into szetszho:develop Jul 22, 2026
6 checks passed
@szetszho

Copy link
Copy Markdown
Owner

Hey, could you start another pull request to the main branch?

@CollieIsCute

Copy link
Copy Markdown
Author

Hi @szetszho , do you plan to submit any further CJK UI-related PRs upstream, especially the batch-prewarm and font-cache improvements from this PR? If not, I can rebase these changes onto the latest upstream develop branch and open a PR directly against crosspoint-reader/crosspoint-reader, so we can avoid duplicating work.

@szetszho

szetszho commented Jul 25, 2026 via email

Copy link
Copy Markdown
Owner

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants