-
Notifications
You must be signed in to change notification settings - Fork 6k
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
Enhance image_filter_layer caching to filter a cached child #17175
Enhance image_filter_layer caching to filter a cached child #17175
Conversation
This PR has moved out of the WIP stage and is proposed for merging. Note that there is still a new |
b6a1699
to
9af0a97
Compare
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.
It might be good to add 2 tests to this:
-
A unit test in engine to verify
MergedContainerLayer
,ImageFilteredLayer
, andOpacityLayer
's behavior regarding having a single child, and reuse the same single child for raster cache if possible. -
A more sensitive device lab performance test in framework to guard performance regressions. The
flutter_gallery__transition
test is too insensitive to this raster cache case that we missed the regression before (Transition performance regressed slightly due to OpacityLayer raster cache miss flutter#52864).
flow/layers/image_filter_layer.cc
Outdated
|
||
if (!context->has_platform_view && context->raster_cache && | ||
SkRect::Intersects(context->cull_rect, paint_bounds())) { | ||
SkMatrix ctm = matrix; | ||
#ifndef SUPPORT_FRACTIONAL_TRANSLATION | ||
ctm = RasterCache::GetIntegralTransCTM(ctm); | ||
#endif | ||
context->raster_cache->Prepare(context, this, ctm); | ||
if (render_count_ >= MINIMUM_FILTER_CACHE) { |
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.
It might be nice to move the render_count >= threshold
logic into raster_cache.cc
where similar threshold logic happens for picture raster cache. In the future, we might want to provide a unified mechanism to modify the threshold, or start/disable raster caching immediately if developers give a signal for that.
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.
I didn't do this (for now?) mainly because this is an either/or situation - either prepare the layer itself for caching or prepare the cacheable child for the alternate strategy. Such a mechanism would have to have some thought on whether or not the prepare failed due to minimum render count or whether it failed because of some other intrinsic property (transform not cacheable, layer too small or too big, etc.)
Rather than solve that issue now, I think I'm going to stick with the manual test here in the Preroll method and we can come back around to a general minimum frame count later.
@flar : are you still working on this? |
yes |
My current related task is to extract just the fix to At that point I will push that PR on its own and then revisit this PR as turning that proof of concept into the general |
9af0a97
to
d728eed
Compare
This PR is in its final form ready to be merged. I ran one last set of AB runs of the ImageFiltered Transform devicelab benchmark normalized against the same base engine SHA of the most recent (at the time) engine roll and got the following results (A == framework engine, B == local engine build with fix):
|
For #1, we already have such tests for For #2, we have flutter/flutter#58277 |
I added unit tests for MergedContainerLayer. Since it doesn't expose its GetChildContainer and GetCacheableChild methods publicly the tests just confirm that it produces the same output as if it were a normal ContainerLayer (I copy/pasted the Simple and Multiple tests of ContainerLayer and just instantiate a MergedContainerLayer instead). I also added the same caching tests for ImageFilterLayer as were added for OpacityLayer when the merged container mechanism was originally developed. |
I was able to successfully rerun the |
Pinging for final reviews... |
ed87966
to
fb3263a
Compare
Note that the latest push has a nearly complete rewrite of the MergedContainerLayer comments in container_layer.h, best to reread the final version rather than look at the commit-to-commit diffs. |
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.
LGTM with a nit. Great documentations!
flow/layers/image_filter_layer.cc
Outdated
// from frame to frame so we try to cache the layer itself | ||
// for maximum performance. | ||
TryToPrepareRasterCache(context, this, matrix); | ||
} else if ((transformed_filter_ = filter_->makeWithLocalMatrix(matrix))) { |
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.
Nit: it's probably very easy for someone to think transformed_filter_ = filter_->makeWithLocalMatrix(matrix)
is a typo for transformed_filter_ == filter_->makeWithLocalMatrix(matrix)
. A clearer approach might be
if (render_count >= ...) {
// ...
TryToPrepareRasterCache(context, this, matrix);
return;
}
transformed_filter_ = filter_->makeWithLocalMatrix(matrix);
if (transformed_filter_) {
...
}
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.
Yes, I was going to do something similar and something inside me said "check it out, we could do it all in one line" and I didn't think about the maintenance issue.
I rewrote it similarly - I used an else instead of a return in case someone wants to later add more logic to Preroll after this section and doesn't spot the early return. An early return would be good if that block represented an action that would prevent any further logic, such as detecting an error condition, but in the case above, it is used to avoid the children caching case. Using an else clause keeps it self contained better - it's the "try the layer, or try the children as a backup" block chained into one overall statement.
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.
To be clear - I moved the assignment inside the body of the else clause and made it separate from the if statement that tested its return value:
} else {
transformed_filter_ = filter_->makeWithLocalMatrix(matrix);
if (transformed_filter_) {
I think I'll add a comment about not being able to filter the children if the supplied ImageFilter can't "makeWithLocalMatrix"...
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.
There was also an implicit logic flaw fixed in the rewrite. If the filter transform combination fails, we should still increment the render count which that last version wasn't doing. The new version increments the render count regardless of the outcome of combining the filter and transform.
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.
Changes pushed in latest commit.
2b6506e
to
151a6d4
Compare
This is a nearly complete prototype to enhance the optimization of
ImageFilterLayer
in a manner similar toOpacityLayer
. While implementing this prototype I discovered (and fixed) a bug in the way thatOpacityLayer
manages the caching of its child(ren). This prototype:breaks out the "single child" code from
OpacityLayer
into a shareable utility layer calledMergedContainerLayer
enhances that implementation to better extract a single
Layer
to be used for caching that avoids the problem where every new merged layer contains a brand new uniqueContainerLayer
child.reworks
ImageFilterLayer
to use the new mechanismminor changes to
OpacityLayer
logic to use a better child for cachingalso includes a number of debug log strings to track the work done by
ImageFilterLayer
and the layer cacheincludes an
INFO
log string when the caching assumptions in the newMergedContainerLayer::GetCacheableChild
method failThe new caching mechanism can speed up opacity layers on cached children by 40% or so, possibly more if the child is more than just a single
Picture
. Similar effects are seen on the image filter layers, especially when animating the filter on a static child.Actual test results will be compiled when I settle on a test that best represents what app developers might encounter in the wild.