Closed Bug 1252711 Opened 9 years ago Closed 9 years ago

Headers scrolling on digg.com are not smooth

Categories

(Firefox for Android Graveyard :: Toolbar, defect)

All
Android
defect
Not set
normal

Tracking

(firefox46 unaffected, firefox47 unaffected)

RESOLVED WORKSFORME
Tracking Status
firefox46 --- unaffected
firefox47 --- unaffected

People

(Reporter: kbrosnan, Assigned: jnicol)

References

Details

(Keywords: regression, Whiteboard: [gfx-noted])

Attachments

(1 file)

1) Open http://www.digg.com 2) perform a fling 3) header and footer flicker on and off Nexus 5 running Android 6.0, this is smooth on Aurora
Summary: Headers scrolling on digg.com is not smooth → Headers scrolling on digg.com are not smooth
I can reproduce this easily. WebIDE says the header is position:fixed, so something has gone horribly wrong. I wonder if the AGR budget stuff would have affected this?
Flags: needinfo?(jnicol)
gfx regression with fixed position layers
Flags: needinfo?(milan)
OK, let's get the regression range.
Flags: needinfo?(milan)
Whiteboard: [gfx-noted]
Bug 1231538 seems like the likely culprit.
Blocks: 1231538
Flags: needinfo?(jnicol)
I can't repro this either on current nightly, current aurora, or the march 02 nightly. Maybe the site changed. Kevin/Snorp, can you still repro? If so, can you save the page (assuming it still repro's there) and attach it?
Flags: needinfo?(snorp)
Flags: needinfo?(kbrosnan)
Flags: needinfo?(kbrosnan)
Can you save the page and repro it on that?
Attached file 1252711-digg-save.zip
Yes. I can repro after pushing that folder to /sdcard/. I'll see what I can do about minimizing it some.
I can't repro using that zip either. I unpacked it to http://people.mozilla.org/~kgupta/bug/1252711/ to try it out. At this point I'm thinking it has to do the with layerizing budget, on my smaller-screen Nexus 4 we can layerize the things properly but on higher-resolution devices we don't layerize the same way and that's causing issues.
The AGR budget only affects AGRs which are created due to animated offset or margin properties, not position:fixed. I highly doubt it's making a difference here. FWIW I can reproduce this on my 720x1280 Sony Z5 compact.
Seeing as I can reproduce I'll take a deeper look at this.
Assignee: nobody → jnicol
Oops. Snorp, Kevin: had you guys reduced your layers.max-active value? Mine was at 3 but resetting it to 20 "fixed" this for me. This page ideally would only have 3 layers though - the main layer, the header, and the footer. But instead each news story is getting its own layer, so I'll have a look into why.
Flags: needinfo?(kbrosnan)
(Sorry for comment spam.) But the bigger phone screen on my nexus 6 means that there are more than 20 stories on the page, so we reach the 20 limit anyway, so don't layerize the header and footer. That explains why Kats cannot reproduce on his Nexus 4, but why it does affect larger phones.
Flags: needinfo?(snorp)
Flags: needinfo?(kbrosnan)
Ugh, the card css class has transform-style:preserve-3d, so we need to layerize each one separately. Perhaps since they're not actually 3d transformed we could use inactive layers, however? Matt? If not then I'm not sure what to do about this ticket - We could raise layers.max-active. I'm unsure if enforcing layers.max-active (bug 1231818) actually helped in terms of OOMs very much. We were talking about lowering it (bug 1252929), which might help with memory usage but will also make problems like this much more common.
Flags: needinfo?(matt.woodrow)
(In reply to Jamie Nicol [:jnicol] from comment #16) > Ugh, the card css class has transform-style:preserve-3d, so we need to > layerize each one separately. Perhaps since they're not actually 3d > transformed we could use inactive layers, however? Matt? Letting preserve-3d be inactive sounds fine, and probably what we want for things that aren't actually changing. If a subtree of preserve-3d contains only 2d transforms, then preserve-3d isn't actually doing anything, so we should be fine to flatten that subtree. If we don't have a a 3d transform on a given nsDisplayTransform, and all descendants are all 2d (RequiredLayerStateForChildren checks this), then we should be able to be inactive and flatten at that point. We'd need to remove the check for mIsTransformSeparator in GetLayerState too. Thinker, does that sound right to you?
Flags: needinfo?(matt.woodrow) → needinfo?(tlee)
For reference, the document looks like the standard two sided 'card' setup: <div perspective> <div preserve-3d> <div 'front' preserve-3d> // Front content </div> <div 'back' preserve-3d rotateY(180deg) backface-hidden> // Back content </div> </div> </div> I'm not sure what this is actually for, they don't seem to use a flip animation for anything I've tried. We build a lot of layers for this, and they never get used for anything (and never have 3d), so we should not do this.
(In reply to Matt Woodrow (:mattwoodrow) from comment #17) > (In reply to Jamie Nicol [:jnicol] from comment #16) > > Ugh, the card css class has transform-style:preserve-3d, so we need to > > layerize each one separately. Perhaps since they're not actually 3d > > transformed we could use inactive layers, however? Matt? > > Letting preserve-3d be inactive sounds fine, and probably what we want for > things that aren't actually changing. > > If a subtree of preserve-3d contains only 2d transforms, then preserve-3d > isn't actually doing anything, so we should be fine to flatten that subtree. > > If we don't have a a 3d transform on a given nsDisplayTransform, and all > descendants are all 2d (RequiredLayerStateForChildren checks this), then we > should be able to be inactive and flatten at that point. > > We'd need to remove the check for mIsTransformSeparator in GetLayerState too. > > Thinker, does that sound right to you? It looks right. We may need to change GetLayerState() of nsDisplayPerspective and nsDisplayOpacity, too. I am also thinking doing it without flattening items if nsDisplayTransform::Paint() is implemented by manipulating CTM of DrawTarget and painting children.
Flags: needinfo?(tlee)
Matt, Thinker, I tried to implement what you suggested in comment 17, but after doing that none of the cards were visible any more. I could see in the display list dump that we did start using basic layers to render them, but also a lot of the layers were marked [not visible]. Maybe I just didn't follow your instructions properly, but any ideas why that might be the case?
Okay, I have worked out why nothing gets rendered using inactive layers - the visibility of a nested preserve3d nsDisplayTransform will not get computed, due to https://dxr.mozilla.org/mozilla-central/source/layout/base/nsDisplayList.cpp#6150. So it's treated as invisible and is not painted. Commenting out that line means that we render the cards, but presumably that line serves a purpose. Can/should that condition be changed given that the condition in nsDisplayTransform::GetLayerState() is being changed? If we can resolve that then we can paint the front face of the cards without crazy layerization. However, because the back face of the cards has a 3D transform they must be actively layerized. That makes sense to me. But because of RequiredLayerStateForChildren we end up not just layerizing the card backface div, but also its parent the card div. I don't understand why this is necessary, do we need to layerize all ancestors of a 3D transform? is this deliberate or a bug? If deliberate: is this too early to determine that the backface layer will be invisible, because backface-visibility=hidden, so we can therefore not layerize the parent.
Flags: needinfo?(tlee)
Flags: needinfo?(matt.woodrow)
Jamie, do you ever try to change this line https://dxr.mozilla.org/mozilla-central/source/layout/base/nsDisplayList.cpp#5978? Remove Combines3D*() and mIsTransformSeparator. I wrote this line to be more defensive at that time. I think we can change it, now. We can not set any inactive layer in between two active layers on the tree of the same 3D rendering context. If we do so, the transform of an active layer that rotate 90 degree along x or y would hide the content that is rotated to visible by another active layer.
Flags: needinfo?(tlee)
(In reply to Thinker Li [:sinker] from comment #22) > Jamie, do you ever try to change this line > https://dxr.mozilla.org/mozilla-central/source/layout/base/nsDisplayList. > cpp#5978? Remove Combines3D*() and mIsTransformSeparator. Sorry, I should have been clearer. I already have made that change based on comment 17. Doing that caused nothing to get rendered, however. This is because of the line of code I linked to in comment 21, the nsDisplayTransform (and all its children) get computed as invisible so never get rendered. Commenting that line out causes the page to be rendered correctly again. Is that line still necessary, or is there a way around that?
Flags: needinfo?(tlee)
Could you point out which line checks the visible of an item, and remove the item from being painted? For items with active layers, this line https://dxr.mozilla.org/mozilla-central/source/layout/base/FrameLayerBuilder.cpp?case=true&from=FrameLayerBuilder.cpp#3858 makes sure layers participating preserve-3d context are always drawn. You need some similar code to force painting/or build inactive layer for items. Consider a container layer with a transform of rotation along X-axis for 90 degree. It's bounds would be empty, but one of its ancestors may rotate it back. So, you will not get correct bounds for some cases.
Flags: needinfo?(tlee)
(In reply to Jamie Nicol [:jnicol] from comment #21) > Okay, I have worked out why nothing gets rendered using inactive layers - > the visibility of a nested preserve3d nsDisplayTransform will not get > computed, due to > https://dxr.mozilla.org/mozilla-central/source/layout/base/nsDisplayList. > cpp#6150. So it's treated as invisible and is not painted. > > Commenting out that line means that we render the cards, but presumably that > line serves a purpose. Can/should that condition be changed given that the > condition in nsDisplayTransform::GetLayerState() is being changed? As thinker pointed out, the 'true' bounds of an intermediate node in a preserve-3d might be empty when represented as a 2d rect, but not be empty in 3d. Since we can't necessarily come up with a reasonable result for GetBounds(), we always just return empty and then make sure FrameLayerBuilder and the layers code know to treat it as visible regardless. Check to see if we're missing a call to Layer::Extend3DContext() in the inactive layer rendering path? > > If we can resolve that then we can paint the front face of the cards without > crazy layerization. However, because the back face of the cards has a 3D > transform they must be actively layerized. That makes sense to me. But > because of RequiredLayerStateForChildren we end up not just layerizing the > card backface div, but also its parent the card div. I don't understand why > this is necessary, do we need to layerize all ancestors of a 3D transform? > is this deliberate or a bug? If deliberate: is this too early to determine > that the backface layer will be invisible, because > backface-visibility=hidden, so we can therefore not layerize the parent. Why does the back face have a 3d transform? Shouldn't it be fully flipped, so it's just reversed in 2d? If any layer is active, then all of its ancestors must be active too. We can only have inactive within active, not the reverse.
Flags: needinfo?(matt.woodrow)
> Check to see if we're missing a call to Layer::Extend3DContext() in the inactive layer rendering path? BasicLayerManager::PaintSelfOrChildren() checks Layer::IsVisible() which checks Layer::Extend3DContext() so that's not the problem. We do try to paint the layer. The problem is that FrameLayerBuilder::DrawPaintedLayer() does not call RecomputeVisibilityForItems() for inactive layers, because the visibility should already have been computed. But because we use an empty bounds, the item has an empty visibility.
(In reply to Jamie Nicol [:jnicol] from comment #26) > > Check to see if we're missing a call to Layer::Extend3DContext() in the inactive layer rendering path? > > BasicLayerManager::PaintSelfOrChildren() checks Layer::IsVisible() which > checks Layer::Extend3DContext() so that's not the problem. We do try to > paint the layer. The problem is that FrameLayerBuilder::DrawPaintedLayer() > does not call RecomputeVisibilityForItems() for inactive layers, because the > visibility should already have been computed. But because we use an empty > bounds, the item has an empty visibility. Right, so can we make FrameLayerBuilder::PaintItems bypass the visibility test if the item is a preserve-3d one?
> Right, so can we make FrameLayerBuilder::PaintItems bypass > the visibility test if the item is a preserve-3d one? We could do this, but the nsDisplayItem::Paint() implementation will probably not work correctly because its visible rect is wrong. We could make FrameLayerBuilder::DrawPaintedLayer() call RecomputeVisibilityForItems() when aLayer->GetParent()->Combines3DTransformWithAncest1ors(). That seems to work. To go back to an earlier question you asked: > Why does the back face have a 3d transform? > Shouldn't it be fully flipped, so it's just reversed in 2d? Matrix4x4::Is2D() treats this transform as 3d because _33 == -1.0f instead of 1.0f. Changing that condition to "abs(_33) != 1.0f" works. Should this be done? With those changes the page renders, but the card backface is also rendered. I suppose we will now also need to handle backface-visibility=hidden somewhere.
Flags: needinfo?(matt.woodrow)
(In reply to Jamie Nicol [:jnicol] from comment #28) > > Right, so can we make FrameLayerBuilder::PaintItems bypass > > the visibility test if the item is a preserve-3d one? > > We could do this, but the nsDisplayItem::Paint() implementation will > probably not work correctly because its visible rect is wrong. > > We could make FrameLayerBuilder::DrawPaintedLayer() call > RecomputeVisibilityForItems() when > aLayer->GetParent()->Combines3DTransformWithAncest1ors(). That seems to work. Shouldn't it only be the intermediate nsDisplayTransform items that have empty visible regions? nsDisplayTransform paints using PaintInactiveLayer, it doesn't have a ::Paint() function at all. > > To go back to an earlier question you asked: > > Why does the back face have a 3d transform? > > Shouldn't it be fully flipped, so it's just reversed in 2d? > > Matrix4x4::Is2D() treats this transform as 3d because _33 == -1.0f instead > of 1.0f. Changing that condition to "abs(_33) != 1.0f" works. Should this be > done? > > With those changes the page renders, but the card backface is also rendered. > I suppose we will now also need to handle backface-visibility=hidden > somewhere. Ah, right. That's a little harder, we've always treated 3d as active and we have reftests that rely on this to be useful.
Flags: needinfo?(matt.woodrow)
Jamie, do you know, or can you find out if this is realistically a "bug fix", or a fundamental problem that will require significant effort and code change?
Flags: needinfo?(jnicol)
I am struggling a bit with it, but as far as I can tell this should be fixable.
Flags: needinfo?(jnicol)
Digg have changed their website so this is no longer an issue, so I'm going to close this as worksforme. However, I've opened bug 1274528 for ensuring too many active layers doesn't break async scrolling. And bug 1274532 for using inactive layers for 2d preserve3d transforms.
Status: NEW → RESOLVED
Closed: 9 years ago
Resolution: --- → WORKSFORME
See Also: → 1274528, 1274532
Product: Firefox for Android → Firefox for Android Graveyard
You need to log in before you can comment on or make changes to this bug.

Attachment

General

Created:
Updated:
Size: