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

Backface support #1606

Merged
merged 4 commits into from Sep 12, 2017
Merged

Backface support #1606

merged 4 commits into from Sep 12, 2017

Conversation

@mephisto41
Copy link
Contributor

mephisto41 commented Aug 24, 2017

This solves #1419 .


This change is Reviewable

@glennw
Copy link
Member

glennw commented Aug 24, 2017

I'll look at this in detail tomorrow (if @kvark doesn't get to it first!) but on a quick read this seems to change a lot more interface functions than I was expecting. backface-visibility only affects transformable elements, so it should only apply at the stacking context level, shouldn't it?

@mephisto41
Copy link
Contributor Author

mephisto41 commented Aug 24, 2017

The backface-visibility also effects on both transformable and non-transformable items. For transformable items (a.k.a stacking context), If backface-visibility is false and transform of stacking context is in backface, then we drop entire stacking context.

For non-transformable items, if backface-visibility is false, we'll check its belonging stacking context is in backface or not.

So that we need add backface visibility for every display item. I know there are quite many interface changes. But I don't find any other suitable way to do this.

@glennw
Copy link
Member

glennw commented Aug 24, 2017

We shouldn't need that many interface changes then - we should be operate at the stacking context level and skip the whole stacking context in that case.

Looking at the way we detect if a stacking context is not visible and skipping it is probably a good place to use as a reference, I think.

@glennw
Copy link
Member

glennw commented Aug 24, 2017

I'll have a bit more of a think about how to best represent the per-element visibility flag. Perhaps we can store it in the primitive run, since it's so rarely used...

@bors-servo
Copy link
Contributor

bors-servo commented Aug 24, 2017

The latest upstream changes (presumably #1597) made this pull request unmergeable. Please resolve the merge conflicts.

@kvark
kvark approved these changes Aug 24, 2017
Copy link
Member

kvark left a comment

I think the API changes are reasonable, given that any element can have the flag, not just SC.

We just need to introduce a helper structure to pass down everywhere and hide such common elements, e.g.

struct PrimitiveInfo {
  rect: LayoutRect,
  local_clip: Option<LocalClip>,
  is_backface_visible: bool,
}
@@ -540,16 +548,21 @@ impl DisplayListBuilder {
debug_assert_eq!(len, count);
}

pub fn push_rect(&mut self, rect: LayoutRect, local_clip: Option<LocalClip>, color: ColorF) {
pub fn push_rect(&mut self,

This comment has been minimized.

@kvark

kvark Aug 24, 2017

Member

FYI, this is the old formatting style

@jrmuizel
Copy link
Contributor

jrmuizel commented Aug 24, 2017

Would this be better with a push/pop_back_face_visibilty or set_default_back_face_visibility item? That avoids having to encode it in every item.

@mstange
Copy link
Contributor

mstange commented Aug 24, 2017

Morris, do you have a testcase where backface-visibility is applied (and has an effect on) a non-transformable item? The spec seems to say "Applies to: transformable elements", so I'd have thought that specifying it on stacking contexts would be enough.

@mephisto41
Copy link
Contributor Author

mephisto41 commented Aug 24, 2017

Spec says the transformable elements are "an element whose layout is governed by the CSS box model". So I think the transformable elements are not only indicate stacking context but also indicate all elements that apply box model.

From gecko's implementation perspective, we can query IsBackfaceHidden from all kind of display items. So I think backface visibility apply to almost every elements.

@mstange
Copy link
Contributor

mstange commented Aug 24, 2017

I can only see us call BackfaceIsHidden() from the nsDisplayTransform display item, not from any random display item.

I've tried to create a testcase where backface-visibility: hidden was respected on a non-transformed element, and was only able to do that by setting transform-style: preserve-3d on the transformed parent element. And in that case we will wrap the children into nsDisplayTransform items, which will translate into webrender stacking contexts, as far as I know. (I found bug 1393554 while creating that testcase...)

Can you create a testcase that proves me wrong? I agree with you now that the spec doesn't really call this out explicitly. Maybe @mattwoodrow knows more details.

@mattwoodrow
Copy link
Contributor

mattwoodrow commented Aug 24, 2017

For items that create their own layers we call nsDisplayItem::BackfaceIsHidden (which calls nsIFrame::BackfaceIsHidden). For items that go into a PaintedLayer we call nsIFrame::In3DContextAndBackfaceIsHidden and use the result to make sure we split PaintedLayers when this changes.

I think for a testcase, you can create a preserve-3d scene where a node has multiple untransformed children, and backface-visibility: hidden is set on one of them.
We should end up create a single 'wrapper' nsDisplayTransform around the set, but will hopefully split the PaintedLayer and set the CONTENT_BACKFACE_HIDDEN flag on the right layer.

The spec is definitely not very clear here (and having backface-visibility: hidden apply to untransformed elements is a pain), but iirc thinker implemented it this way to match WebKit.

@mephisto41
Copy link
Contributor Author

mephisto41 commented Aug 25, 2017

Testcase for individual item. https://jsfiddle.net/r1Luxv0y/

@mephisto41
Copy link
Contributor Author

mephisto41 commented Aug 25, 2017

I also dumped display list and layer in gecko for above test case. I also turned on force active layer to prevent div merged to painted layer.

Painting --- after optimization:
CanvasBackgroundColor p=0x11239a028 f=0x11795f318(Canvas(html)(-1)) bounds(0,0,57540,54540) layerBounds(0,0,57540,54540) visible(0,0,57540,54540) componentAlpha(0,0,0,0) clip(0,0,57540,54540) asr() clipChain(<0,0,57540,54540> [root asr]) uniform ref=0x11795e960 agr=0x11795f318 (opaque 0,0,57540,54540) (rgba 255,255,255,255) layer=0x11285dc00
nsDisplayTransform p=0x11239a6e8 f=0x117c2fb98(Block(div)(1) id:outer) bounds(480,480,3000,18000) layerBounds(480,480,3000,18000) visible(0,0,57540,54540) componentAlpha(0,0,0,0) clip(0,0,57540,54540) asr() clipChain(<0,0,57540,54540> [root asr]) ref=0x11795e960 agr=0x11795f318[ 1 0 0 0; 0 -1 0 0; -0 -0 -1 -0; 16 616 0 1; ] extends-3d-context layer=0x113d9b400
  nsDisplayTransform p=0x11239a518 f=0x117c2fb98(Block(div)(1) id:outer) bounds(0,0,3000,18000) layerBounds(0,0,3000,18000) visible(0,0,3000,18000) componentAlpha(0,0,0,0) clip() asr() clipChain() ref=0x117c2fb98 agr=0x117c2fb98 (opaque 0,0,3000,18000)[ I ] transform-separator 3d-context-leaf extends-3d-context layer=0x113d9fc00
    BackgroundColor p=0x11239a0c8 f=0x117c35818(Block(div)(1) id:rect1) bounds(0,0,3000,3000) layerBounds(0,0,3000,3000) visible(0,0,3000,18000) componentAlpha(0,0,0,0) clip(0,0,3000,3000) asr() clipChain(<0,0,3000,3000> [root asr]) uniform ref=0x117c2fb98 agr=0x117c2fb98 (opaque 0,0,3000,3000) (rgba 1,0,0,1) layer=0x113daa400
    BackgroundColor p=0x11239a180 f=0x117c35bd0(Block(div)(3) id:rect2) bounds(0,3000,3000,3000) layerBounds(0,3000,3000,3000) visible(0,0,3000,18000) componentAlpha(0,0,0,0) clip(0,3000,3000,3000) asr() clipChain(<0,3000,3000,3000> [root asr]) uniform ref=0x117c2fb98 agr=0x117c2fb98 (opaque 0,3000,3000,3000) (rgba 0,0.501961,0,1) layer=0x113e72c00
    BackgroundColor p=0x11239a238 f=0x117c35ed8(Block(div)(5) id:rect3) bounds(0,6000,3000,3000) layerBounds(0,6000,3000,3000) visible(0,0,3000,18000) componentAlpha(0,0,0,0) clip(0,6000,3000,3000) asr() clipChain(<0,6000,3000,3000> [root asr]) uniform ref=0x117c2fb98 agr=0x117c2fb98 (opaque 0,6000,3000,3000) (rgba 0,0,1,1) layer=0x113e73800
    BackgroundColor p=0x11239a2f0 f=0x117c361e0(Block(div)(7) id:rect4) bounds(0,9000,3000,3000) layerBounds(0,9000,3000,3000) visible(0,0,3000,18000) componentAlpha(0,0,0,0) clip(0,9000,3000,3000) asr() clipChain(<0,9000,3000,3000> [root asr]) uniform ref=0x117c2fb98 agr=0x117c2fb98 (opaque 0,9000,3000,3000) (rgba 1,1,0,1) layer=0x1144e1000
    BackgroundColor p=0x11239a3a8 f=0x117c364e8(Block(div)(9) id:rect5) bounds(0,12000,3000,3000) layerBounds(0,12000,3000,3000) visible(0,0,3000,18000) componentAlpha(0,0,0,0) clip(0,12000,3000,3000) asr() clipChain(<0,12000,3000,3000> [root asr]) uniform ref=0x117c2fb98 agr=0x117c2fb98 (opaque 0,12000,3000,3000) (rgba 0.501961,0,0.501961,1) layer=0x1144eb800
    BackgroundColor p=0x11239a460 f=0x117c367f0(Block(div)(11) id:rect6) bounds(0,15000,3000,3000) layerBounds(0,15000,3000,3000) visible(0,0,3000,18000) componentAlpha(0,0,0,0) clip(0,15000,3000,3000) asr() clipChain(<0,15000,3000,3000> [root asr]) uniform ref=0x117c2fb98 agr=0x117c2fb98 (opaque 0,15000,3000,3000) (rgba 0,0,0,1) layer=0x11499f800
Painting --- layer tree:
ClientLayerManager (0x10f810400) --- in content order
  ClientContainerLayer (0x112347400) [visible=< (x=0, y=0, w=1918, h=1818); >] [opaqueContent]
    ClientColorLayer (0x11285dc00) [clip=(x=0, y=0, w=1918, h=1818)] [visible=< (x=0, y=0, w=1918, h=1818); >] [color=rgba(255, 255, 255, 1.000000)] [bounds=(x=0, y=0, w=1918, h=1818)]
    ClientContainerLayer (0x113d9b400) [clip=(x=0, y=0, w=1918, h=1818)] [transform=[ 1 0 0 0; 0 -1 0 0; 0 0 -1 0; 16 616 0 1; ]] [effective-transform=[ 1 0 0 0; 0 -1 0 0; 0 0 -1 0; 16 616 0 1; ]] [visible=< (x=0, y=0, w=100, h=600); >] [extend3DContext]
      ClientContainerLayer (0x113d9fc00) [effective-transform=[ 1 0 0 0; 0 -1 0 0; 0 0 -1 0; 16 616 0 1; ]] [visible=< (x=0, y=0, w=100, h=600); >] [opaqueContent] [combines3DTransformWithAncestors] [is3DContextLeaf] [usesTmpSurf]
        ClientColorLayer (0x113daa400) [clip=(x=0, y=0, w=100, h=100)] [visible=< (x=0, y=0, w=100, h=100); >] [backfaceHidden] [color=rgba(255, 0, 0, 1.000000)] [bounds=(x=0, y=0, w=100, h=100)]
        ClientColorLayer (0x113e72c00) [clip=(x=0, y=100, w=100, h=100)] [visible=< (x=0, y=100, w=100, h=100); >] [color=rgba(0, 128, 0, 1.000000)] [bounds=(x=0, y=100, w=100, h=100)]
        ClientColorLayer (0x113e73800) [clip=(x=0, y=200, w=100, h=100)] [visible=< (x=0, y=200, w=100, h=100); >] [backfaceHidden] [color=rgba(0, 0, 255, 1.000000)] [bounds=(x=0, y=200, w=100, h=100)]
        ClientColorLayer (0x1144e1000) [clip=(x=0, y=300, w=100, h=100)] [visible=< (x=0, y=300, w=100, h=100); >] [color=rgba(255, 255, 0, 1.000000)] [bounds=(x=0, y=300, w=100, h=100)]
        ClientColorLayer (0x1144eb800) [clip=(x=0, y=400, w=100, h=100)] [visible=< (x=0, y=400, w=100, h=100); >] [backfaceHidden] [color=rgba(128, 0, 128, 1.000000)] [bounds=(x=0, y=400, w=100, h=100)]
        ClientColorLayer (0x11499f800) [clip=(x=0, y=500, w=100, h=100)] [visible=< (x=0, y=500, w=100, h=100); >] [color=rgba(0, 0, 0, 1.000000)] [bounds=(x=0, y=500, w=100, h=100)]

As you can see the layer tree, we have 3 colors layer with backface hidden and others with backface visible.

@glennw
Copy link
Member

glennw commented Aug 25, 2017

@mephisto41 I think the suggestion from @jrmuizel

Would this be better with a push/pop_back_face_visibilty or set_default_back_face_visibility item? That avoids having to encode it in every item.

might work well. That way we don't have to change many interfaces at all, and we only pay the storage cost when that flag changes value. It could be encoded efficiently within the primitive runs. Does that sound like it would work?

@mephisto41
Copy link
Contributor Author

mephisto41 commented Aug 25, 2017

@glennw I think this might be good idea. I'll implement it. Thanks for the suggestion.

@kvark
Copy link
Member

kvark commented Aug 25, 2017

@jrmuizel @glennw
FWIW, I'm not a fan of introducing more states just to save the old API. PrimitiveInfo struct proposed above feels more flexible and future-compatible. Bincode could spend as much as 1 bit for the flag, so it shouldn't be a concern from the data size point of view.

@jrmuizel
Copy link
Contributor

jrmuizel commented Aug 25, 2017

There's some further discussion about what the semantics of backface-visiblity in https://bugzilla.mozilla.org/show_bug.cgi?id=1393554

@glennw
Copy link
Member

glennw commented Aug 27, 2017

@kvark Maybe you're right, having thought about it a bit over the weekend. @mephisto41 Sorry for all the back and forth - perhaps having it in the API per-primitive, but as a PrimitiveInfo struct (like @kvark mentioned above) so it is expandable in the future is the way to go...

@mephisto41
Copy link
Contributor Author

mephisto41 commented Aug 29, 2017

Sure, I'll implement new patch as @kvark suggested! Thanks.

@mephisto41
Copy link
Contributor Author

mephisto41 commented Aug 30, 2017

@kvark Some questions when adding PrimitiveInfo

  1. The parameters of push_xxx api in webrender_api only include rect, local_clip and is_backface_visible but struct DisplayItem contains a additional member which is clip_and_scroll: ClipAndScrollInfo. Should I include clip_and_scroll in the PrimitiveInfo?

  2. In DisplayItem, we use LayoutRect for rect. But in frame_builder, we use LayerRect for primitive. Is there any problem here if we pass down PrimitiveInfo everywhere? Since this might not be a problem because LayoutRect is typedef of LayerRect. But I want to sure this doesn't have any potential problem.

@kvark
Copy link
Member

kvark commented Aug 30, 2017

@mephisto41 those are good questions:

Should I include clip_and_scroll in the PrimitiveInfo?

Hmm, it comes down to a question - to what extend would we want the statefull-ness of the API to be preserved. I'd say, let's leave the clip_and_scroll member/stack API untouched, for now at least. We can take another look at it after your change is in.

In DisplayItem, we use LayoutRect for rect. But in frame_builder, we use LayerRect for primitive.

Unless someone tells us that one of those types is on the way out, I'd prefer us keeping the distinction. The comment says:

For now layout pixels are equivalent to layer pixels, but it may change.

In order to include PrimitiveInfo in the DisplayItem you can parametrize it (generically) by the rectangle space type, i.e.:

struct PrimitiveInfo<P> {
  rect: TypedRect<f32, P>,
  ...
}
type LayoutPrimitiveInfo = PrimitiveInfo<LayoutPixel>;
@mephisto41 mephisto41 force-pushed the mephisto41:backface-support branch from fe2b9b1 to c76ec7f Sep 6, 2017
@mephisto41
Copy link
Contributor Author

mephisto41 commented Sep 7, 2017

r? @kvark

Copy link
Member

kvark left a comment

Thank you! It's shaping up nicely :)

@@ -1559,6 +1563,7 @@ impl StackingContext {
isolation,
is_page_root,
is_visible: false,
is_backface_visible: is_backface_visible,

This comment has been minimized.

@kvark

kvark Sep 7, 2017

Member

nit: the greatness of rust allows you to write just is_backface_visible, now, DRY

}

pub fn local_clip(&self) -> &LocalClip {
&self.iter.cur_item.local_clip
pub fn get_layer_primitive_info(&self, offset: &LayoutVector2D) -> LayerPrimitiveInfo {

This comment has been minimized.

@kvark

kvark Sep 7, 2017

Member

great abstraction now

"line" => self.handle_line(dl, item, local_clip),
"image" => self.handle_image(dl, wrench, item, local_clip),
"text" | "glyphs" => self.handle_text(dl, wrench, item, local_clip),
"rect" => self.handle_rect(dl, item, local_clip, backface_visible),

This comment has been minimized.

@kvark

kvark Sep 7, 2017

Member

we could probably make all of those handlers to receive LayoutPrimitiveInfo with zeroed out rectangle, for simplicity

}

pub fn local_clip_with_offset(&self, offset: &LayoutVector2D) -> LocalClip {
self.iter.cur_item.local_clip.create_with_offset(offset)
pub fn local_clip(&self) -> Option<LocalClip> {

This comment has been minimized.

@kvark

kvark Sep 7, 2017

Member

In all but one case this function result is unwrapped right away, and it used to return LocalClip directly. Would be simpler to keep it this way and change less code, I think.

@bors-servo
Copy link
Contributor

bors-servo commented Sep 7, 2017

The latest upstream changes (presumably #1667) made this pull request unmergeable. Please resolve the merge conflicts.

@mephisto41 mephisto41 force-pushed the mephisto41:backface-support branch 4 times, most recently from f00f11f to 59bf291 Sep 8, 2017
@bors-servo
Copy link
Contributor

bors-servo commented Sep 12, 2017

The latest upstream changes (presumably #1679) made this pull request unmergeable. Please resolve the merge conflicts.

@mephisto41 mephisto41 force-pushed the mephisto41:backface-support branch from 59bf291 to afbc931 Sep 12, 2017
@glennw
Copy link
Member

glennw commented Sep 12, 2017

@kvark @mephisto41 Is this one ready to go, or are there still a couple of comments to be addressed?

@mephisto41
Copy link
Contributor Author

mephisto41 commented Sep 12, 2017

@glennw, I think this one is ready. But I leave the call to @kvark. @kvark, Do you think this is ready?

@kvark
Copy link
Member

kvark commented Sep 12, 2017

@mephisto41 I'm happy :) thank you!
@bors-servo r+

@bors-servo
Copy link
Contributor

bors-servo commented Sep 12, 2017

📌 Commit afbc931 has been approved by kvark

@bors-servo
Copy link
Contributor

bors-servo commented Sep 12, 2017

Testing commit afbc931 with merge b1fea4f...

bors-servo added a commit that referenced this pull request Sep 12, 2017
Backface support

This solves #1419 .

<!-- Reviewable:start -->
---
This change is [<img src="https://reviewable.io/review_button.svg" height="34" align="absmiddle" alt="Reviewable"/>](https://reviewable.io/reviews/servo/webrender/1606)
<!-- Reviewable:end -->
@bors-servo
Copy link
Contributor

bors-servo commented Sep 12, 2017

☀️ Test successful - status-travis
Approved by: kvark
Pushing b1fea4f to master...

@bors-servo bors-servo merged commit afbc931 into servo:master Sep 12, 2017
2 of 3 checks passed
2 of 3 checks passed
continuous-integration/travis-ci/pr The Travis CI build could not complete due to an error
Details
continuous-integration/appveyor/pr AppVeyor build succeeded
Details
homu Test successful
Details
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Linked issues

Successfully merging this pull request may close these issues.

None yet

7 participants
You can’t perform that action at this time.