New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
More efficient decoding for down-sampled Flutter images using cache(Width|Height) #15372
Conversation
@chinmaygarde This is re: memory pressure that customer:dream is experiencing so they want us to review it asap. Thanks! |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
We should add some tests for this, particularly around where the codec can support efficient sub-sampling and what happens when we request different aspect ratios (whether we decide to preserve aspect ratio or not).
The infra failures on this patch are not related to changes here. @godofredoc have we added mac try builders? If so we have to configure them to have the right credentials to access infra_internal in CIPD. |
Ah sorry, looks like the permissions issue got fixed since this PR was added. It should succeed on a rerun/new commit. |
The permission problems were fixed this morning at ~12:00PM PST. If you retry the mac builders should not fail the installation of xcode. |
Do you have a pointer to pre-existing tests and whether they can already provide coverage? Right now, the change is a semantic noop. On a tangent, writing unit-tests is also non-trivial since the functions in questions have internal linkage. |
The tests for this are in lib/ui/painting/image_decoder_unittests.cc We should add a test to that which would have failed before this change. There are a few ways we could make this code a bit more testable. One is to move all of the resize logic into some other class that we properly test, and pass in an instance of it to I would lean towards refactoring at least some of the private static logic into another object that can just be given to the ImageDecoder, but I'm certainly open to other ideas :) |
I'm taking suggestions. My implementation deliberately doesn't change the observable behavior other than saving resources. W/o e.g. a mock skia backend that let's us assert backend API calls, it's not clear to me how we would test it. Maybe you know a SkImage API to test if a SkImage_Lazy implementation is expanded? |
I think it would make sense to just test that our call to @chinmaygarde may have better ideas. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This optimization was present in the engine for a hot minute before we realized that SkCodec::MakeFromData
does not return dimensions after account for EXIF orientation while SkImage::MakeFromEncoded
does apply EXIF related rotation (introduced in ad582b5#diff-5c1f6e0e5e35e3a93e44256d86f61627R167 and reverted in fd2cb81#diff-5c1f6e0e5e35e3a93e44256d86f61627R136). It looks to me that the same issue is present here as well in that the decoded_image
will return dimensions with EXIF rotation information applied while the codec will not. TBH, I am finding it a bit hard to reason about what happens when something like fixtures/Horizontal.jpg
is scaled down. To make this clearer and easier to test, we could modify ImageDecoder::ImageResult
to return more information about the intermediates during image decompression. That would make it easier to write unit-tests for this in image_decode_unittests.cc
.
Another thing that initially tripped me up was the fact that this patch relies on SkImage::MakeFromEncoded
relying on a lazy image being created. This seems like an implementation detail in Skia that makes the code here a bit hard to follow. A suggestion would be to create the codec once, apply any EXIF related orientation corrections, resize down to a size supported by the codec and then perform the final resize to the correct size if necessary. In this model, each step is clear and the overall rubric easier to follow.
Good call. You're right that the codec doesn't respect the orientation. I'm now using a slightly higher level abstraction: ImageGenerator, which does support orientation. Note that ImageGenerator is in skia's sources so might be considered a private API. Unfortunately, there's really no other API to use, i.e. images don't support efficient sub-pixel decoding. That said, I'm reasonably confident that we can talk to skia folks especially since I added a unit-test to ensure we respect exif orientation.
I moved the decoding bits now out of
This should be the case now. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
A few nits but this is looking good.
…idth|Height) When down-scaling images, decode encoded images into smaller images closer to the target size before finally down-scaling them to their target size. For very large images this can avoid inflating the image into its full size first before throwing it away. This can help to significantly reduce peak memory utilization. On a tangent, we could be even more efficient, if we'd interpret the cache(Width|Height) as sizing hints. I also opportunistically added warnings, I don't think a "caching" API should support scaling images up or changing their aspect ratio.
… cache(Width|Height) (flutter/engine#15372)
… cache(Width|Height) (flutter/engine#15372) (#48986) flutter/engine@e0fe834...c7d0fb7 git log e0fe834..c7d0fb7 --first-parent --oneline 2020-01-16 ignatz@users.noreply.github.com More efficient decoding for down-sampled Flutter images using cache(Width|Height) (flutter/engine#15372) If this roll has caused a breakage, revert this CL and stop the roller using the controls here: https://autoroll.skia.org/r/flutter-engine-flutter-autoroll Please CC jimgraham@google.com on the revert to ensure that a human is aware of the problem. To report a problem with the AutoRoller itself, please file a bug: https://bugs.chromium.org/p/skia/issues/entry?template=Autoroller+Bug Documentation for the AutoRoller is here: https://skia.googlesource.com/buildbot/+/master/autoroll/README.md
…idth|Height) (flutter#15372) When down-scaling images, decode encoded images into smaller images closer to the target size before finally down-scaling them to their target size. For very large images this can avoid inflating the image into its full size first before throwing it away. This can help to significantly reduce peak memory utilization. On a tangent, we could be even more efficient, if we'd interpret the cache(Width|Height) as sizing hints. I also opportunistically added warnings, I don't think a "caching" API should support scaling images up or changing their aspect ratio.
… cache(Width|Height) (flutter#15372)" This reverts commit 4e8a88d.
…idth|Height) (flutter#15372) When down-scaling images, decode encoded images into smaller images closer to the target size before finally down-scaling them to their target size. For very large images this can avoid inflating the image into its full size first before throwing it away. This can help to significantly reduce peak memory utilization. On a tangent, we could be even more efficient, if we'd interpret the cache(Width|Height) as sizing hints. I also opportunistically added warnings, I don't think a "caching" API should support scaling images up or changing their aspect ratio.
When down-scaling images, decode encoded images into smaller images
closer to the target size before finally down-scaling them to their
target size. For very large images this can avoid inflating the image
into its full size first before throwing it away. This can help to
significantly reduce peak memory utilization.
On a tangent, we could be even more efficient, if we'd interpret the
cache(Width|Height) as sizing hints.
I also opportunistically added warnings, I don't think a "caching" API
should support scaling images up or changing their aspect ratio.