Skip to content

OpacityLayer causes sharp increases in memory usage in trivial apps #75697

@ds84182

Description

@ds84182

Reproduction code (only depends on dart:ui and a large image):

https://gist.github.com/ds84182/2621bab595f769474be9971fbfab89b4

Copied from the documentation comment on main():


Raw, dart:ui-only example of an issue with opacity layers & the rasterizer cache.

Works best on Desktop, controls need to be changed for Android & iOS.
Web untested.

Controls:

Left Click - Add Opacity Layer
Right Click - Remove Opacity Layer

Expected Behavior:

Adding opacity layers does not increase the total memory usage.

Actual Behavior:

Each opacity layer causes the RasterCache to allocate a new 4,000 by 2,000 texture.

One would expect that nested opacity layers would collapse into a single (equivalent) layer, and one would expect that a picture layer displaying a simple image could merge with the opacity layer, resulting in a call to drawImage with a Paint applying the equivalent opacity.

Why does this matter?

There are many scenarios where a developer can place one (or more) opacity layers around a simple image. For example, the built-in FadeInImage widget, which uses FadeTransition, which eventually produces an opacity layer in RenderAnimatedOpacity.

Apps fade in images a lot. An image-heavy application (e.g. our application) can vary in memory usage wildly, regardless of the Flutter-level ImageCache.

Additionally, an app with an infinite scrolling list of images being faded in will hit memory limits on fast flings. Not because of image loading, but because the raster cache is unbounded when used in conjunction with OpacityLayer. This is also why video memory usage can seemingly fluctuate, or fail to match the exact number of images loaded.

What can be done?

OpacityLayer currently delegates out to the RasterCache, drawing the child layer with the specified paint.

https://github.com/flutter/engine/blob/ecfe5aec50e5a8d97e4818addbcef47f7300d2d1/flow/layers/opacity_layer.cc#L63

This can be changed to allow layers to:

  1. Trivially combine paints (OpacityLayer -> OpacityLayer situation)
  2. Circumvent the need for the raster cache by combining paints together, if compatible (OpacityLayer -> simple PictureLayer situation)

Perhaps an ImageLayer can be introduced, to make this situation easier to optimize, since Skia Pictures are opaque (can't get list of Ops out).


cc @dnfield
also goodnight, it's 1:30am.

Metadata

Metadata

Assignees

No one assigned

    Labels

    P2Important issues not at the top of the work listc: new featureNothing broken; request for a new capabilityc: performanceRelates to speed or footprint issues (see "perf:" labels)engineflutter/engine related. See also e: labels.perf: memoryPerformance issues related to memory

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions