Skip to content

WIP/discussion: Pre-render clipped texts#6233

Draft
MaxGyver83 wants to merge 3 commits intofyne-io:developfrom
MaxGyver83:prerender-text-below-clip-area
Draft

WIP/discussion: Pre-render clipped texts#6233
MaxGyver83 wants to merge 3 commits intofyne-io:developfrom
MaxGyver83:prerender-text-below-clip-area

Conversation

@MaxGyver83
Copy link
Copy Markdown
Contributor

Description:

Dragging (scrolling) isn't smooth in Fyne apps with a lot of text on Android.

I have visualized how long *driver.handlePaint calls take while dragging up a large RichText widget (generated from about 35 kB of Markdown). Test app: https://gist.github.com/MaxGyver83/1022b44649df8ac4b69dce6a352f0e28

2026-03-26 20 13 09 1121x629 Region

The green areas show when my finger touches the screen (for dragging).

When new lines become visible, painter.DrawString is called for every new line and takes about 6 ms per line. A paint call shouldn't take more than 16 ms. Thus, with 3 new lines this goal can't be reached and dragging begins to stutter. (Dragging back in the other direction works better because then all texts are already cached.)

It would be cool if we could speed up DrawString itself. But I don't know how.

My next best idea is to pre-render (=draw strings into textures) during idle times. This PR tries to do this. When text is below the clip area (but not to far away), p.createTextTexture is called which check if the text is already cached, and if not, it creates such a texture using fyne.Do. AFAIU, this happens between *driver.handlePaint calls when there is some free time left, reading from d.queudFuncs.Out():

		for {
			select {
			case <-draw.C:
				d.sendPaintEvent()
			case fn := <-d.queuedFuncs.Out():
				fn()

Unfortunately, this fix doesn't work as expected. Dragging feels different now but still not good.

Here is a visualization:

2026-03-26 20 38 26 902x617 Region

Do you have any ideas how this could be improved (or what to try instead)?

(This PR is work-in-progress and mainly for discussion.)

Checklist:

  • Tests included.
  • Lint and formatter run with no errors.
  • Tests all pass.

Where applicable:

  • Public APIs match existing style and have Since: line.
  • Any breaking changes have a deprecation path or have been discussed.
  • Check for binary size increases when importing new modules.
@coveralls
Copy link
Copy Markdown

coveralls commented Mar 26, 2026

Coverage Status

coverage: 60.289% (-0.1%) from 60.393% — MaxGyver83:prerender-text-below-clip-area into fyne-io:develop

@andydotxyz
Copy link
Copy Markdown
Member

This same discussion could be applied to List/Table etc with similar reuse dynamics.

However I'm not convinced it will make a difference...if you have 5 visible rows and want to scroll past 20 we currently:

Render 5 and then 20 as we scroll.

If you render the next one as well it becomes:

Render 6 and then 20 as we scroll.

So I'm not sure it resolves the issue you describe.

@MaxGyver83
Copy link
Copy Markdown
Contributor Author

MaxGyver83 commented Mar 29, 2026

I see. I have pushed a better solution:

The new commit adds a low priority queue. After the first paint event, a new function checks for all text objects if they are clipped and not yet cached. If so, they are pushed to the low priority queue. Then, when the process idles, it pre-renders texts from the queue.

This results in quite smooth scrolling. (Probably, pre-rendering finishes faster in combination with #6236.)

plot-prerender-dont-wait2

What do you think about this? Is it too complicated? If you think it's useful, it probably can be further improved:

  • When to fill the queue (only once or every time the canvas content changes)?
  • Fill the queue in the already existing c.WalkTrees(draw, afterDraw) in paintWindow?
  • Include images?
@MaxGyver83 MaxGyver83 changed the title WIP/discussion: Pre-render text below clip area Mar 29, 2026
@redawl redawl mentioned this pull request Apr 6, 2026
3 tasks
@andydotxyz
Copy link
Copy Markdown
Member

This is a very compelling improvement now.
I wonder how the performance compares develop with this suggestion with all the other optimisations that have landed.
The complexity is quite high, but if we show it to be worth it then let's try to figure the right balance of look-ahead vs scroll smoothness

@MaxGyver83
Copy link
Copy Markdown
Contributor Author

I wonder how the performance compares develop with this suggestion with all the other optimisations that have landed.

@andydotxyz , @redawl: This is how it looks on the develop branch, currently:

smooth-scrolling-develop
@MaxGyver83
Copy link
Copy Markdown
Contributor Author

MaxGyver83 commented Apr 10, 2026

Now, DrawString takes around 1.66 ms on average (before around 6 ms).

$ average DrawStringFiltered.log
717 numbers, average: 1661.3585285913528 µs
Add a new low-priority queue for functions and push the pre-rendering of
clipped texts to this queue (only once). Process queue when idle.
@MaxGyver83 MaxGyver83 force-pushed the prerender-text-below-clip-area branch from 7f86e45 to 5beb1b7 Compare April 11, 2026 06:58
@MaxGyver83
Copy link
Copy Markdown
Contributor Author

This is with this PR's branch rebased to develop:

smooth-scrolling-prerender
@redawl
Copy link
Copy Markdown
Contributor

redawl commented Apr 11, 2026

IMO this is a lot of complexity, while we know there is still room to improve raw performance

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

Labels

None yet

4 participants