Repository navigation
Add RasterCache option to RepaintBoundary widget #32662
Description
Use case
Although RepaintBoundary is a key widget in improving Flutter’s performance on the UI thread, it’s not well-utilized on the GPU thread. For example, only the lowest-level RepaintBoundary, which corresponds to leaf-level engine PictureLayer, currently triggers the raster cache on the GPU thread. In certain scenarios, raster caching can make a big performance difference -- e.g., 20%-30% in our home_scroll_perf average_frame_rasterizer_time_millis benchmark.
With the recent introduction of retained EngineLayers, we now have the opportunity to use RepaintBoundary to trigger more raster caching on the higher level RepaintBoundary. Similar to the recent opacity and PhysicalShapeLayer optimizations, we may be able to improve our performance by multiple times.
Proposal
Add a RasterCache option to the RepaintBoundary widget, so the developer can decide when to engage the optimizations. This option is not only to override our current raster cache heuristics, which are not very smart or reliable, but also allows developers to elect to make the tradeoff between quality and speed themselves. For example, when there’s a rotation transformation above the RepaintBoundary, raster caching the whole RepaintBoundary means that we’re no longer drawing pixel-perfect graphics, but the performance gain may significantly outweigh the quality drop (see #24627).
Things to implement:
- Write performance tests for the scenarios where we’d like to see improvements. This will guide our implementation and check our correctness and performance.
- Add a
RasterCacheoption to theRepaintBoundarywidget and wire it up between the framework and the engine. - Enable the engine-side raster caching based on the option.
- Document the feature, and update Github issues to reflect the performance improvement, to teach our developers how to do it themselves.
Proposed by @liyuqian
Activity
liyuqian commentedon May 14, 2019
Thanks for polishing the proposal! Closing #27952 as a duplicate and add a reference to #27677
Allow RenderObject.layer to by any ContainerLayer
liyuqian commentedon May 28, 2019
Paste Yegor's comment here for convenience: "It sounds a lot like will-change. Wondering if we could just map the proposed RasterCache to it."
arsen commentedon Jul 28, 2019
Any progress on this?
liyuqian commentedon Jul 29, 2019
@flar : I have the same question on the progress 😄
We just received some internal bug b/137395062 last week, and its current solution cl/260167188 is to manually take a snapshot of the blurred widget. Adding RasterCache option to RepaintBoundary widget would:
RepaintBoundaryinstead of 50 lines of code to snapshot manually.flar commentedon Jul 30, 2019
I'm looking into revisiting my RepaintBoundary caching work. Meanwhile I think that @chinmaygarde had some suggestions or feedback on faster ways to snapshot widgets that might help with the solution for b/137395062
jason721017 commentedon Jan 28, 2020
Thanks for the proposal. Any ETA on the implementation?
While we can try to find workaround in the scenario like b/137395062, it is often counter intuitive. It is hard to be certain on what will be raster cached or not. Having an option in RepaintBoundary will be very helpful.
zanderso commentedon Jun 18, 2020
@liyuqian @flar What is the status of this issue? What is the remaining work to do?
flar commentedon Jun 18, 2020
I've been working on various internal caching mechanisms that would work automatically behind the scenes and fixing up errors in our existing caching mechanisms. Since RepaintBoundaries aren't visible to the engine, a significant rework of our layer mechanism would be required to make them visible so that they can control caching.
fredlee12345678 commentedon Jul 29, 2020
@flar I just want to know the status of this issue, is it ready for us?
ravenblackx commentedon Sep 25, 2020
A related suggestion! Rather than a boolean RasterCache option, how about a cacheStrategy option, with
CacheStrategy.defaultbeing the current behavior of "trying to figure out the best option itself",cacheStrategy.rasterfor the people wanting that previously in this bug, and for me,CacheStrategy.noCache, which paints the child the normal way on the existing buffer as if RepaintBoundary wasn't present.The distinction between
CacheStrategy.noCacheand not having aRepaintBoundaryis for the case of non-overlapping widgets, let's sayanimatingWidgetandstaticWidget[1-10]. With noRepaintBoundary, all tenstaticWidgets repaint every frame because animatingWidget is updating. With aRepaintBoundaryonanimatingWidgetthere's a buffer allocated foranimatingWidget; the buffer isn't providing anything useful becauseanimatingWidgetis repainting every frame anyway, but theRepaintBoundaryis preventing the other widgets from repainting when they don't need it. So theRepaintBoundaryserves a purpose, but the buffer does not.The buffer, in this case, incurs a nontrivial memory cost and maybe also a performance cost.
Ibtesam-Mahmood commentedon Jun 29, 2022
@liyuqian @flar Is there any update on this issue?
mj-hd commentedon Sep 29, 2022
As a workaround, I created a widget that attaches cache hints (isComplex, willChange) to the current/parent RepaintBoundary like this:
fzyzcjy commentedon Oct 1, 2022
Hi, I wonder whether the
toPictureSyncwill solve this problem?Hixie commentedon May 2, 2024
The raster cache is a legacy feature at this point (no plans on adding it to impeller) so I'm going to close this.
github-actions commentedon May 16, 2024
This thread has been automatically locked since there has not been any recent activity after it was closed. If you are still experiencing a similar issue, please open a new bug, including the output of
flutter doctor -vand a minimal reproduction of the issue.