Skip to content

Conversation

@mephisto41
Copy link
Contributor

@mephisto41 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

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

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

Copy link
Member

@kvark kvark left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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,
}

}

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

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

FYI, this is the old formatting style

@jrmuizel
Copy link
Collaborator

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

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

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

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

@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
Collaborator

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

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

@mephisto41
Copy link
Contributor Author

@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
Copy link
Contributor Author

r? @kvark

Copy link
Member

@kvark kvark left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thank you! It's shaping up nicely :)

isolation,
is_page_root,
is_visible: false,
is_backface_visible: is_backface_visible,
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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 {
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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),
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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> {
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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

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

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

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

@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

@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

📌 Commit afbc931 has been approved by kvark

@bors-servo
Copy link
Contributor

⌛ Testing commit afbc931 with merge b1fea4f...

bors-servo pushed 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

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

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

7 participants