Skip to content

perf/fix: on-screen previews first, cache trim, text-search restart, abandoned-load leak - #729

Open
palmoni5 wants to merge 3 commits into
espresso3389:masterfrom
palmoni5:perf/render-order-search-and-load-fixes
Open

palmoni5 wants to merge 3 commits into
espresso3389:masterfrom
palmoni5:perf/render-order-search-and-load-fixes

Conversation

@palmoni5

Copy link
Copy Markdown
Contributor

Summary

Three independent fixes found while profiling a reader app built on PdfViewer. Each is its own commit.

1. Render on-screen previews first; trim the image cache once per paint

  • Order. Preview renders queue on one lock per cache and are served in call order. The paint loop walks pages by index, so with a vertical cache extent the page above the viewport was requested before the page on screen, and the visible page waited behind it. Requests are now collected during the loop and issued by distance from the visible rect (0 for pages that overlap it).
  • Eviction. removeCacheImagesIfCacheBytesExceedsLimit removed an image before checking the budget, so every call evicted at least one out-of-extent preview even when the cache was under maxImageBytesCachedOnMemory. It was also called once per page inside the paint loop. Out-of-extent previews were therefore dropped on almost every paint and re-rendered on scroll-back. The budget is now checked before each removal, and the trim runs once after the loop.

2. PdfTextSearcher: re-issuing the running pattern no longer kills the search

  • startTextSearch cancelled the running session first, and search() then returned early because the pattern was identical to the last condition. The matches stayed half collected and isSearching stayed true. This is easy to hit from a search field that fires again with unchanged text. An identical pattern now only drops a pending search for a different pattern. _restartSearch clears the condition so it can still rerun the same pattern after a page's content changes.
  • goToMatch now calls notifyListeners(), so UI such as a "3/120" counter can follow the current match.
  • A new pattern clears currentIndex/currentMatch. The previous pattern's position does not index into the new matches.

3. PdfDocumentListenable: dispose a document whose load finished after eviction

When the last listener is removed while a document is still loading (e.g. an app that tears the viewer down to retry a slow load), the listenable is evicted from the ref cache and the next resolveListenable() creates a new one. The in-flight load still completed into the evicted listenable via setDocument, where nothing could reach the document or dispose it. The load now disposes the document when its listenable is no longer the registered one.

Testing

  • test/pdf_text_searcher_test.dart (new):

    • re-issuing the running pattern completes the search with all matches;
    • a different pattern still replaces the running search;
    • goToMatchOfIndex notifies listeners;
    • a new pattern clears the previous position.

    The first and third fail without the fix.

  • test/pdf_document_ref_abandoned_load_test.dart (new):

    • a load that finishes after every listener left disposes its document (fails without the fix);
    • a load with a listener attached keeps its document.
  • Fix 1 has no dedicated test: the preview queue and image cache are private, and render tracing goes to developer.log. It is covered by the existing viewer tests.

  • packages/pdfrx: PDFIUM_PATH=... flutter test: all 48 tests passed, including lazy_loading_test.dart against real PDFium.

  • flutter analyze lib test: clean apart from the pre-existing info in test/lazy_loading_test.dart.

…r paint

The preview lock serves requests in call order and the paint loop walks pages by index, so with a cache extent the page above the viewport was rendered before the one on screen. Requests are now collected during the loop and issued by distance from the visible rect.

removeCacheImagesIfCacheBytesExceedsLimit removed an image before checking the budget, and it ran once per page inside the loop. Out-of-extent previews were evicted even under budget and re-rendered on scroll-back. The budget is now checked first and the trim runs once after the loop.
…sued

startTextSearch cancelled the running session before search() returned early for an identical pattern, so the matches stayed half collected and isSearching stuck at true. An identical pattern now only drops a pending search for another pattern; _restartSearch clears the condition so it can still rerun the same pattern.

goToMatch now notifies listeners, so UI such as a "3/120" counter can follow the current match, and a new pattern clears the position left by the previous one.
…ter eviction

When the last listener leaves while a document is loading (e.g. a viewer torn down to retry a slow load), the listenable is evicted and the next resolve builds a new one. The in-flight load still completed into the evicted listenable, where nothing could reach or dispose the document. The load now disposes it instead.
palmoni5 added a commit to palmoni5/otzaria that referenced this pull request Sep 27, 2026
הענף מכיל את עיגון התצוגה באותו פריים (espresso3389/pdfrx#724) ואת התיקונים של espresso3389/pdfrx#729: רינדור העמודים שעל המסך לפני שכניהם, פינוי מטמון התמונות רק מעל התקציב, חיפוש טקסט שלא נקטע בשליחה חוזרת של אותה שאילתה ומודיע במעבר בין התאמות, ושחרור מסמך שטעינתו הסתיימה אחרי שכל המאזינים עזבו.

חוזרים ל-pub.dev רק אחרי ששני ה-PR ימוזגו וישוחררו.
Y-PLONI pushed a commit to palmoni5/otzaria that referenced this pull request Sep 28, 2026
הענף מכיל את עיגון התצוגה באותו פריים (espresso3389/pdfrx#724) ואת התיקונים של espresso3389/pdfrx#729: רינדור העמודים שעל המסך לפני שכניהם, פינוי מטמון התמונות רק מעל התקציב, חיפוש טקסט שלא נקטע בשליחה חוזרת של אותה שאילתה ומודיע במעבר בין התאמות, ושחרור מסמך שטעינתו הסתיימה אחרי שכל המאזינים עזבו.

חוזרים ל-pub.dev רק אחרי ששני ה-PR ימוזגו וישוחררו.

This branch has not been deployed

No deployments
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.

1 participant