Skip to content
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

Add Clip enum and default to not clip #18057

Closed
Hixie opened this issue May 31, 2018 · 14 comments
Closed

Add Clip enum and default to not clip #18057

Hixie opened this issue May 31, 2018 · 14 comments
Assignees
Labels
c: API break Backwards-incompatible API changes c: regression It was better in the past than it is now engine flutter/engine repository. See also e: labels.

Comments

@Hixie
Copy link
Contributor

Hixie commented May 31, 2018

The following should just show a rotated black small box:

      new Center(
	child: new RepaintBoundary(
	  child: new Container(
	    color: Colors.white,
            child: new Padding(
              padding: const EdgeInsets.all(100.0),
              child: new SizedBox(
		height: 100.0,
                width: 100.0,
                child: new Transform.rotate(
	          angle: 1.0, // radians                                                                                        
	          child: new ClipRect(
                    child: new Container(
                      color: Colors.red,
                      child: new Container(
                        color: Colors.white,
                        child: new RepaintBoundary(
                          child: new Center(
                            child: new Container(
                              color: Colors.black,
                              height: 10.0,
	                      width: 10.0,
                            ),
                          ),
                        ),
                      ),  
                    ),
                  ),
	        ),
	      ),
            ),
          ),
	),
      ),

Instead it renders like this:

@Hixie Hixie added c: regression It was better in the past than it is now engine flutter/engine repository. See also e: labels. dependency: skia Skia team may need to help us labels May 31, 2018
@Hixie Hixie added this to the Goals milestone May 31, 2018
@Hixie
Copy link
Contributor Author

Hixie commented May 31, 2018

cc @chinmaygarde @abarth @liyuqian
See also the bugs and PRs linked to above.

@tvolkert
Copy link
Contributor

Can we add a golden file test that covers this?

@liyuqian
Copy link
Contributor

Oh, I see: the saveLayer should be applied after clipPath to prevent the artifacts. In the code that I removed in flutter/engine#5420 , the saveLayer is applied before clipPath. That only pays the performance cost without solving the artifacts:

https://fiddle.skia.org/c/71acea8432e19235d836b6e4c9c71b42

@reed-at-google
Copy link

reed-at-google commented May 31, 2018 via email

@liyuqian
Copy link
Contributor

I think that saveLayer is so costly and the artifact is such an edge case that we shouldn't make the saveLayer the default solution inside our ClipPathLayer or PhysicalShapeLayer. If we have clients that do want to avoid such artifacts, maybe we should let them explicitly state that (e.g., add a SaveLayerWidget beneath ClipPathLayer) and tell them that such widgets are costly in performance.

@liyuqian
Copy link
Contributor

Thanks @reed-at-google for clarifying that. I've just also reproduced it in https://fiddle.skia.org/c/36043f79c796cdbdae85ffa68fca5274

BTW, do you know what do Android and Chrome do with those artifacts? Do they insert a saveLayer after each clipPath?

@Hixie
Copy link
Contributor Author

Hixie commented May 31, 2018

Ah, yes, this is something that we got wrong early in the project. I fixed it for the cases where we did it wrong in Dart but I forgot to check the C++ code for similar errors.

I strongly feel that our clip primitives should do the right thing. There's no point us having APIs if they give bad output.

I agree that doing the right thing for clipping is expensive. I think we should therefore consider if we could redesign the framework to avoid clipping entirely unless explicitly requested. Right now we clip speculatively to "be on the safe side", e.g. the Card widget will clip because it may have a background image, but might not. IMHO we should consider removing all explicit clips in the framework that aren't demonstrably always necessary, and instead suggest that developers who want clipping should opt into it. So for example, we could add a clip property to Card that turns on clipping for cards.

This would obviously be a major breaking change, but if the performance benefits are as serious as we suspect, then it's worth it.

@liyuqian
Copy link
Contributor

  1. Avoiding unnecessary clip (especially a complex one) is always a good thing to do regardless of whether we have saveLayer or not.

  2. The artifacts are not specific to clipPath. It's universal to all AA paths: https://fiddle.skia.org/c/3e08883e8a1fbe97155f39e04ff0ad68
    Therefore, I don't see a particular reason to treat clipPath specially by adding a saveLayer. We probably should always notify our users that there would be such artifacts if they try to render paths with shared edges in AA mode. That said, if clipPath without a saveLayer is the wrong thing to do, then we also did the drawPath wrong; if we think it's OK to drawPath without saveLayer, then it's probably Ok to clipPath without saveLayer.

@Hixie
Copy link
Contributor Author

Hixie commented Jun 1, 2018

I don't think #2 above is accurate. We were previously not doing the saveLayer logic right, but when done right, for clipping, there's no artifacts.

I think we should revert flutter/engine#5420, fix the saveLayer/clip logic to actually do the right thing, and then we should go through the framework and change it so that it never clips unless it's definitely necessary (e.g. a viewport) or someone explicitly opts in (e.g. with "clip: true" on a Material or DecoratedBox, or anything that uses those under the hood and where we want to support clipping, like Card, Container, etc).

@liyuqian
Copy link
Contributor

liyuqian commented Jun 1, 2018

Ok, this is a complicated issue with a big tradeoff between performance and quality. Let's have an offline discussion with those who're interested in this topic. Before that, I arranged a meeting with Skia team to see how Android/Chrome's compositor handles this and what their recommendations are.

Please let me know who I should add to our offline discussion. @Hixie , @tvolkert , @chinmaygarde , @cbracken , @xster , or maybe @abarth , @eseideGooglel ?

@liyuqian
Copy link
Contributor

liyuqian commented Jun 1, 2018

We decided to

  1. Remove clip as much as possible and make no-clip the default behavior
  2. Expose the following 3 enums when there’s a clip:
    1. No AA
    2. AA without saveLayer
    3. AA with saveLayer

Shall we close this issue after implementing all these enums?

@liyuqian liyuqian added the c: API break Backwards-incompatible API changes label Jun 3, 2018
@liyuqian
Copy link
Contributor

liyuqian commented Jun 3, 2018

Note that “AA without saveLayer” and “AA with saveLayer” have semantically different behaviors so we can’t make decisions for our clients (i.e., automatically adding or removing saveLayer is not a choice for us).

Below are 2 fiddles that illustrate “without saveLayer” and “saveLayer” could have different semantics:

  1. https://fiddle.skia.org/c/83ed46ceadaf90f36a4df3b98cbe1c35
  2. https://fiddle.skia.org/c/704acfa049a7e99fbe685232c45d1582

Note that the difference depends on the blend mode. For many blend modes, they’re associative (i.e., A blend B blend C = A blend (B blend C)) so adding a saveLayer may not be immediately problematic. But there’s no guarantee that all blend modes are associative (at least there’s no guarantee from Skia’s documentation). Moreover, each draw may have a different blend mode which could break the associative property even if every blend mode by itself is associative (see, e.g., the fiddles above).

Therefore, we shouldn’t automatically add saveLayer for our clients. That’s both costly in performance and error-prone.

@Hixie
Copy link
Contributor Author

Hixie commented Jun 3, 2018

Sounds good.

@liyuqian liyuqian removed the dependency: skia Skia team may need to help us label Jun 15, 2018
@liyuqian liyuqian self-assigned this Jun 18, 2018
liyuqian added a commit to liyuqian/flutter that referenced this issue Jun 20, 2018
This is a follow up on flutter/engine#5420
and flutter#18057

As you can see from the diff, we also mistakenly saveLayer before
the clip at some places previously.
liyuqian added a commit that referenced this issue Jun 21, 2018
This is a follow up on flutter/engine#5420
and #18057

As you can see from the diff, we also mistakenly saveLayer before
the clip at some places previously.
@liyuqian liyuqian changed the title Clips aren't saving layers correctly Add Clip enum and default to not clip Jun 27, 2018
liyuqian added a commit to liyuqian/engine that referenced this issue Jun 28, 2018
liyuqian added a commit to liyuqian/engine that referenced this issue Jun 29, 2018
liyuqian added a commit to flutter/engine that referenced this issue Jun 29, 2018
bkonyi pushed a commit to flutter/engine that referenced this issue Jul 13, 2018
liyuqian added a commit that referenced this issue Aug 3, 2018
See details in our proposal for this breaking API change and #18057. This PR setup all code paths to allow the change but doesn't change the clip behavior by itself. We'll change `defaultClipBehavior` from `Clip.antiAlias` to `Clip.none` in the following PR to change the clip behavior and update tests.
liyuqian added a commit to flutter/goldens that referenced this issue Aug 3, 2018
The new golden files reflects Clip enum and will check the bleeding
edge artifact as mentioned in:

flutter/flutter#18057 (comment)
liyuqian added a commit to flutter/engine that referenced this issue Aug 4, 2018
If we want to avoid the bleeding edge artifact (flutter/flutter#18057 (comment)) using saveLayer, we have to call drawPaint instead of drawPath as anti-aliased drawPath will always have such artifacts.

This is discovered when I try to add golden tests for such bleeding artifacts using our new Clip enum. Here's the updated golden files: flutter/goldens@cb1fa8a?short_path=57b30ce#diff-57b30cea9b10b7ca689009854e12d70e
@github-actions
Copy link

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 -v and a minimal reproduction of the issue.

@github-actions github-actions bot locked as resolved and limited conversation to collaborators Aug 15, 2021
Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.
Labels
c: API break Backwards-incompatible API changes c: regression It was better in the past than it is now engine flutter/engine repository. See also e: labels.
Projects
None yet
Development

No branches or pull requests

4 participants