-
Notifications
You must be signed in to change notification settings - Fork 6k
[Impeller] separate algorithm for computing render target size. #54604
Changes from 1 commit
9d59af1
a935594
7aa3a48
54cea5a
e6e56d8
98a5d68
81ccaa0
55a6a50
3591395
a8f0e2c
1c55cf5
a6f5f21
a646c1b
8daf291
199a846
b2eb6b9
d347783
24442e0
78b7f60
2a4ac2d
3eff88b
04b5be5
086abb8
4a5f30b
fa3afcc
4e7a707
e0105d7
e8f34ac
8a3d1ef
24ef59a
0cae7a8
ae57e62
bc2276e
b3ab833
334e5e9
82a6621
5011b3c
008b6f9
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
- Loading branch information
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -32,37 +32,43 @@ std::optional<Rect> ComputeSaveLayerCoverage( | |
| coverage = Rect::MakeMaximum(); | ||
| } | ||
|
|
||
| // If flood_output_coverage is true, then the restore is applied with a | ||
| // destructive blend mode that requires flooding to the coverage limit. | ||
| // Technically we could only allocated a render target as big as the input | ||
| // coverage and then use a decal sampling mode to perform the flood. Returning | ||
| // the coverage limit is a correct but non optimal means of ensuring correct | ||
| // rendering. | ||
| if (flood_output_coverage) { | ||
| return coverage_limit; | ||
| } | ||
|
|
||
| // The content coverage must be scaled by any image filters present on the | ||
| // saveLayer paint. For example, if a saveLayer has a coverage limit of | ||
| // 100x100, but it has a Matrix image filter that scales by one half, the | ||
| // actual coverage limit is 200x200. | ||
| if (image_filter) { | ||
| // Transform the input coverage into the global coordinate space before | ||
| // computing the bounds limit intersection. This is the "worst case" | ||
| // coverage value before we intersect with the content coverage below. | ||
| std::optional<Rect> source_coverage_limit = | ||
| image_filter->GetSourceCoverage(effect_transform, coverage_limit); | ||
| if (!source_coverage_limit.has_value()) { | ||
| // No intersection with parent coverage limit. | ||
| return std::nullopt; | ||
| } | ||
|
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I'm trying to wrap my head around this. Why aren't we handing in content_coverage? That might be the same as coverage_limit if there was flooding, but it could be less.
Contributor
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Not sure what you mean exactly. A case where this would fire is, you an IF that translates a SL entirely offscreen, or somethng like that.
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. This was a case of clicking on the wrong line. I was referring to the fact that you pass the coverage_limit in to the GetSourceCoverage call and I was pointing out that the content size might be much smaller. But, in typing that out I suddenly realized that the point there is not to compute the actual result, but to compute the "worst case scenario for input pixels" against which you clip the indicated coverage. So, I think this is fine, but I didn't get the explanation on my first reading. The intersection with the source coverage happens right after this comment, so this code is only making the current input coverage smaller - but based on the reverse filter size of the output limit - which means passing in
Contributor
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I moved the comment around a bit so it should be clearer. |
||
| // The image filter may change the coverage limit required to flood | ||
| // the parent layer. Returning the source coverage limit so that we | ||
| // can guarantee the render target is larger enough. | ||
| // | ||
| // See note below on flood_output_coverage. | ||
| if (flood_output_coverage) { | ||
|
||
| return source_coverage_limit; | ||
| } | ||
|
|
||
| // Transform the input coverage into the global coordinate space before | ||
| // computing the bounds limit intersection. | ||
| return coverage.TransformBounds(effect_transform) | ||
| .Intersection(source_coverage_limit.value()); | ||
| } | ||
|
|
||
| // If the input coverage is maximum, just return the coverage limit that | ||
| // is already in the global coordinate space. | ||
| if (coverage.IsMaximum()) { | ||
| // | ||
| // If flood_output_coverage is true, then the restore is applied with a | ||
| // destructive blend mode that requires flooding to the coverage limit. | ||
| // Technically we could only allocated a render target as big as the input | ||
| // coverage and then use a decal sampling mode to perform the flood. Returning | ||
| // the coverage limit is a correct but non optimal means of ensuring correct | ||
| // rendering. | ||
| if (flood_output_coverage | coverage.IsMaximum()) { | ||
| return coverage_limit; | ||
| } | ||
|
|
||
|
|
||
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 think you also need
options.content_is_unbounded().The supplied bounds are still provided because the flag overrides them and we did calculate them, so why not, but they don't promise that the content of this SL is bounded if that flag is present.
If there is unbounded child content, DL might substitute the clip at the moment that content is discovered - but only if that enclosing clip is inside the same DL/layer. If there has not yet been a restricting clip before we get to the unbounded content inside the SL, then the SL itself is marked unbounded via the options.
So, there are 3 conditions that lead to unbounded contents.
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.
The unbounded content should be handled in the line about where I replace the content bounds with std::nullopt:
https://github.com/flutter/engine/blob/main/impeller/display_list/dl_dispatcher.cc#L726-L728
this doesn't solve the problem of impeller/dl speaking different terms of unbounded. Longer term, we should probably just plumb this flag through.
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 handle that by only applying clear color on srcOver, but layer on that is the flood clip check in entity_pass
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.
The unbounded line was deleted. You are pointing to what the code "used to do"...
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 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 have to think more about that. The unbounded is a reason to ignore the bounds, but I'm not sure that user supplied bounds are a reason to use them even in the unbounded case.
Scratches head...
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.
Oh right, we discussed this! That is why I deleted the line, but I need to add it back.
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.
OK, removed bounds from caller. We just check if its unbounded.
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.
agh, I think we hit this already. If a saveLayer contains unbounded content, but does not have a bdf or destructive blend, but the saveLayer was bounded - then bounds.is_clipped should be true and we don't null out the bds.
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.
Okay so this is a case where maybe there is actually a DL misunderstanding. On the test ImageFilteredSaveLayerWithUnboundedContents we expect the content to be clipped via the saveLayer bounds. This saveLayer has a specified bounds of (0, 0, 200, 200) but
options.content_is_unboundedis true.I guess, its technically correct that this saveLayer, which contains a drawPaint, is unbounded - but we decided to treat is bounded so maybe content_is_unbounded should return false?