WebKit Bugzilla
New
Browse
Log In
×
Sign in with GitHub
or
Remember my login
Create Account
·
Forgot Password
Forgotten password account recovery
RESOLVED FIXED
107701
Canvas and DOM go out of sync
https://bugs.webkit.org/show_bug.cgi?id=107701
Summary
Canvas and DOM go out of sync
sugoi
Reported
2013-01-23 09:46:44 PST
This bug was reported in Chromium as Issue 169024, but it was reproduced in other browsers, so it's not a Chromium specific bug. Go to the provided URL and drag the green square using the mouse. The red square will lag behind even though it should be in sync with the green square. Removing the line "overflow: hidden;" from the "overlay" element solves the issue, so the "overflow: hidden;" line triggers this issue somehow.
Attachments
Patch
(1.39 KB, patch)
2013-01-23 09:53 PST
,
sugoi
no flags
Details
Formatted Diff
Diff
Patch
(7.90 KB, patch)
2013-01-29 11:12 PST
,
sugoi
no flags
Details
Formatted Diff
Diff
Patch
(3.43 KB, patch)
2013-01-30 08:27 PST
,
sugoi
no flags
Details
Formatted Diff
Diff
Patch
(5.61 KB, patch)
2013-01-30 13:34 PST
,
sugoi
no flags
Details
Formatted Diff
Diff
Patch
(5.69 KB, patch)
2013-01-31 06:22 PST
,
sugoi
no flags
Details
Formatted Diff
Diff
Patch
(5.53 KB, patch)
2013-01-31 13:12 PST
,
sugoi
no flags
Details
Formatted Diff
Diff
Patch
(5.35 KB, patch)
2013-02-13 14:13 PST
,
sugoi
no flags
Details
Formatted Diff
Diff
Show Obsolete
(6)
View All
Add attachment
proposed patch, testcase, etc.
sugoi
Comment 1
2013-01-23 09:53:02 PST
Created
attachment 184256
[details]
Patch
WebKit Review Bot
Comment 2
2013-01-23 10:48:25 PST
Comment on
attachment 184256
[details]
Patch
Attachment 184256
[details]
did not pass chromium-ews (chromium-xvfb): Output:
http://queues.webkit.org/results/16073324
New failing tests: svg/custom/text-xy-updates-SVGList.xhtml svg/dynamic-updates/SVGUseElement-svgdom-href1-prop.html svg/dynamic-updates/SVGUseElement-dom-href1-attr.html svg/custom/circle-move-invalidation.svg svg/custom/mask-invalidation.svg fast/repaint/search-field-cancel.html fast/repaint/moving-shadow-on-path.html svg/transforms/animated-path-inside-transformed-html.xhtml fast/repaint/inline-relative-positioned.html svg/repaint/repaint-webkit-svg-shadow-container.html
sugoi
Comment 3
2013-01-23 11:08:41 PST
I don't know whether or not it's the correct fix, but it does fix the issue. So, basically, here's the problem I found : There are 2 different codepaths to doing a relayout : 1 ) If you have overflow:visible, then you may write anywhere in your parent's canvas, so your parent has to relayout too. - Doing a relayout all the way up will trigger a relayout of the RenderView object and this will work because everything gets to have a relayout. So here, FrameView::scheduleRelayout() is called from RenderObject::scheduleRelayout(), which was called from RenderObject::markContainingBlocksForLayout(). 2 ) In any other overflow mode, you clip your content to your own canvas, so no need for your parent to relayout, so we stop there. - So since we stop early in RenderObject::markContainingBlocksForLayout(), when we get to RenderObject::scheduleRelayout(), we'll actually call FrameView::scheduleRelayoutOfSubtree(). The fix I wrote is inside that function. It seems it wasn't recursively marking contained objects for relayout when m_layoutSchedulingEnabled is true, for some reason. If the fix isn't the appropriate fix, please explain how the m_layoutSchedulingEnabled logic should work. Thanks
Dimitri Glazkov (Google)
Comment 4
2013-01-23 12:50:35 PST
Comment on
attachment 184256
[details]
Patch One of the things that might be great is your fiddle converted into a layout test. Also, the fix looks wrong -- you're simply invalidating "the whole thing". But I defer to paint experts to guide you here :)
sugoi
Comment 5
2013-01-23 13:00:53 PST
(In reply to
comment #4
)
> (From update of
attachment 184256
[details]
) > One of the things that might be great is your fiddle converted into a layout test.
I have a layout test locally, I'll add it as soon as I know that it validates the "proper" fix.
> Also, the fix looks wrong -- you're simply invalidating "the whole thing". But I defer to paint experts to guide you here :)
I'm not sure I follow you about invalidating "the whole thing". The path where m_layoutSchedulingEnabled is true is the only path not calling "markContainingBlocksForLayout". Does that mean that when m_layoutSchedulingEnabled is false, we always invalidate "the whole thing" ? Also, at the point where this function is called, m_layoutRoot points to the current object (in the reference URL, it would be pointing to the bottom right canvas of the page), which is the root of the subtree and we do want to invalidate the subtree, I believe, since we're about to render it.
James Robinson
Comment 6
2013-01-23 16:44:03 PST
Comment on
attachment 184256
[details]
Patch View in context:
https://bugs.webkit.org/attachment.cgi?id=184256&action=review
> Source/WebCore/page/FrameView.cpp:2258 > + m_layoutRoot->markContainingBlocksForLayout(false);
this basically disables the subtree relayout optimization. The idea of the subtree layout optimization is it's applied when the only layout we need to do is within the subtree. In your test case, likely one of the following is true: 1.) The subtree is incorrectly rooted (too low in the tree) 2.) The subtree optimization shouldn't be used in this instance 3.) The subtree optimization is correct, but something else is awry I would wager it's 3. This call will cover up many sins by causing us to relayout and reinvalidation pretty much the entire world every frame. I think you should try to back out to the narrowest point that's misbehaving. Perhaps something isn't marked as needing layout that should, or perhaps everything is being laid out correctly but something isn't generating correct invalidations (aka repaints in the RenderObject world)
sugoi
Comment 7
2013-01-28 14:17:56 PST
Ok, at this point, I'm stuck, I need the input of someone more knowledgeable than me on the compositing tree. It seems that the compositing tree is incompatible with partial (subtree) layout updates. Here's why : When we call WebCore::RenderLayerModelObject::styleDidChange(), here's what happens : 1) WebCore::RenderLayerModelObject::styleDidChange WebCore::RenderObject::styleDidChange WebCore::RenderObject::setNeedsPositionedMovementLayout WebCore::RenderObject::markContainingBlocksForLayout WebCore::RenderObject::scheduleRelayout WebCore::FrameView::scheduleRelayoutOfSubtree 2) WebCore::RenderLayerModelObject::styleDidChange (same call as stack trace 1) WebCore::RenderLayer::styleChanged WebCore::RenderLayerCompositor::updateLayerCompositingState WebCore::RenderLayerBacking::updateGraphicsLayerConfiguration ... -> Eventually, we read a position value here and set the layer position Unfortunately, the layer position is not updated yet. It will be in the next FrameView::layout() call when, after updating the position value by calling layout on the subtree root, we'll call "updateLayerPositionsAfterLayout()". That call has to happen before the we call updateLayerCompositingState(), otherwise, the layer position will be wrong. I also get a weird behavior when I'm debugging a page that shows this bug. When I refresh it multiple times, I get different results, as if the timers were sometimes getting triggered in a different order. I don't want to implement a solution where I'd call layout(), then updateLayerCompositingState(), then layout(), then updateLayerCompositingState() again, since that would be obviously sub-optimal. I'll wait for someone to chime in and give me some sort of explanation about the expected relationship between the layout operation and the compositing tree before I continue my investigation.
sugoi
Comment 8
2013-01-29 11:12:08 PST
Created
attachment 185271
[details]
Patch
sugoi
Comment 9
2013-01-29 11:24:26 PST
Ok, I tried this new patch to fix the issue. The original fix was breaking the subtree optimization, as James pointed out, so I tried to find something less aggressive, but the current fix may still be too much for what we need, I'll let the reviewers decide. In this version of the fix, only accelerated compositing is affected and it is only affected when a layout call using the subtree optimization causes a layer to move. In that specific case, the position of all layers gets updated so that we don't miss any update before computing the compositing tree. It is slightly redundant, because the subtree will get its position updated twice, potentially, but it's probably better than to update the whole tree more frequently since we use the result of the 1st call to updateLayerPositionsAfterLayout() in order to know if a top layer call is necessary.
Simon Fraser (smfr)
Comment 10
2013-01-29 11:25:52 PST
(In reply to
comment #7
)
> Ok, at this point, I'm stuck, I need the input of someone more knowledgeable than me on the compositing tree. > > It seems that the compositing tree is incompatible with partial (subtree) layout updates. Here's why : > > When we call WebCore::RenderLayerModelObject::styleDidChange(), here's what happens : > > 1) > WebCore::RenderLayerModelObject::styleDidChange > WebCore::RenderObject::styleDidChange > WebCore::RenderObject::setNeedsPositionedMovementLayout > WebCore::RenderObject::markContainingBlocksForLayout > WebCore::RenderObject::scheduleRelayout > WebCore::FrameView::scheduleRelayoutOfSubtree > > 2) > WebCore::RenderLayerModelObject::styleDidChange (same call as stack trace 1) > WebCore::RenderLayer::styleChanged > WebCore::RenderLayerCompositor::updateLayerCompositingState > WebCore::RenderLayerBacking::updateGraphicsLayerConfiguration > ... > -> Eventually, we read a position value here and set the layer position
This part sounds wrong; if we're inside styleChanged (and there's a pending layout), then we can't rely on any geometry information (since layout might change it). The code does do this right now, but what should happen is a subsequent compositing layer update after the next layout should fix it. Is that happening?
Simon Fraser (smfr)
Comment 11
2013-01-29 11:27:28 PST
Comment on
attachment 185271
[details]
Patch View in context:
https://bugs.webkit.org/attachment.cgi?id=185271&action=review
> Source/WebCore/page/FrameView.cpp:1256 > + if (subtree && positionChanged) { > + RenderObject* topRoot = document->renderer(); > + if (topRoot) { > + RenderLayer* topLayer = topRoot->enclosingLayer(); > + topLayer->updateLayerPositionsAfterLayout(rootRenderer(this)->layer(), > + updateLayerPositionFlags(topLayer, false, m_doFullRepaint)); > + } > + }
This doesnt' seem compositing-related. We rely on RenderLayer positions for invalidation etc too.
Simon Fraser (smfr)
Comment 12
2013-01-29 11:44:56 PST
Ah, the bug here is that layer = root->enclosingLayer(); in FrameView::layout() doesn't go high enough up the tree in this case; it needs to find the enclosing stacking container.
Simon Fraser (smfr)
Comment 13
2013-01-29 12:12:00 PST
So that should be layer = root->enclosingLayer()-> enclosingStackingContainer();
sugoi
Comment 14
2013-01-30 08:27:55 PST
Created
attachment 185509
[details]
Patch
sugoi
Comment 15
2013-01-30 08:34:45 PST
I tried to use Simon's solution, but it did not fix the issue. In the cases I've tested, unless we start at the document's root's layer, the bug is still present. The proposed solution works in the cases I've tested with and, unless calling updateLayerPositionsAfterLayout() on the top layer is a terribly time consuming operation, I think/hope this fix could be acceptable, because I currently have no alternative solution :) I'll investigate more if this doesn't work for you.
Build Bot
Comment 16
2013-01-30 09:12:33 PST
Comment on
attachment 185509
[details]
Patch
Attachment 185509
[details]
did not pass mac-wk2-ews (mac-wk2): Output:
http://queues.webkit.org/results/16218587
New failing tests: fast/canvas/canvas-overflow-hidden-animation.html
Simon Fraser (smfr)
Comment 17
2013-01-30 09:19:21 PST
Comment on
attachment 185509
[details]
Patch View in context:
https://bugs.webkit.org/attachment.cgi?id=185509&action=review
> Source/WebCore/page/FrameView.cpp:1197 > - layer = root->enclosingLayer(); > + layer = document->renderer()->enclosingLayer();
This is wrong. You want layer = root->enclosingLayer()->enclosingStackingContainer().
> LayoutTests/ChangeLog:11 > + * fast/canvas/canvas-overflow-hidden-animation.html: Added.
This test is not automatic or self-testing. It needs to detect the incorrect condition without the patch (either with some self-check, or by comparing against a reference).
sugoi
Comment 18
2013-01-30 09:37:36 PST
Comment on
attachment 185509
[details]
Patch View in context:
https://bugs.webkit.org/attachment.cgi?id=185509&action=review
>> Source/WebCore/page/FrameView.cpp:1197 >> + layer = document->renderer()->enclosingLayer(); > > This is wrong. > > You want layer = root->enclosingLayer()->enclosingStackingContainer().
I agree that this is not what we discussed yesterday, but as I mentioned in the comments, "layer = root->enclosingLayer()->enclosingStackingContainer()" does not fix the issue.
Simon Fraser (smfr)
Comment 19
2013-01-30 09:42:45 PST
Hm, interesting. We should figure out why it does not work.
Simon Fraser (smfr)
Comment 20
2013-01-30 10:55:37 PST
(In reply to
comment #15
)
> I tried to use Simon's solution, but it did not fix the issue. In the cases I've tested, unless we start at the document's root's layer, the bug is still present. The proposed solution works in the cases I've tested with and, unless calling updateLayerPositionsAfterLayout() on the top layer is a terribly time consuming operation, I think/hope this fix could be acceptable, because I currently have no alternative solution :) I'll investigate more if this doesn't work for you.
You should use showLayerTree() in the debugger to see what's different between the layer tree between an update from the root, and an update from the enclosingStackingContainer.
sugoi
Comment 21
2013-01-30 11:25:36 PST
Using showLayerTree() shows that both trees (the good one and the bad one) are identical after calling updateCompositingLayersAfterLayout(). There must be something (like a redraw or an invalidation) triggered only by calling updateCompositingLayersAfterLayout() on the top layer.
sugoi
Comment 22
2013-01-30 13:34:45 PST
Created
attachment 185551
[details]
Patch
sugoi
Comment 23
2013-01-30 13:39:02 PST
So I have a new proposal for the fix. I was expecting this new code, or something equivalent, to be done within RenderLayerCompositor::updateCompositingLayers() somewhere, but it seems like there's no guarantee of that happening, so I added it directly in RenderLayer::updateLayerPositionsAfterLayout() since similar code is executed within RenderLayer::updateLayerPositions()
Simon Fraser (smfr)
Comment 24
2013-01-30 13:49:44 PST
Comment on
attachment 185551
[details]
Patch View in context:
https://bugs.webkit.org/attachment.cgi?id=185551&action=review
> Source/WebCore/rendering/RenderLayer.cpp:386 > +#if USE(ACCELERATED_COMPOSITING) > + if ((flags & UpdateCompositingLayers) && rootLayer && (rootLayer != this) && rootLayer->isComposited()) { > + RenderLayerBacking::UpdateAfterLayoutFlags updateFlags = RenderLayerBacking::CompositingChildrenOnly; > + if (flags & NeedsFullRepaintInBacking) > + updateFlags |= RenderLayerBacking::NeedsFullRepaint; > + if (flags & IsCompositingUpdateRoot) > + updateFlags |= RenderLayerBacking::IsUpdateRoot; > + rootLayer->backing()->updateAfterLayout(updateFlags); > + }
What does this do that the later updateCompositingLayersAfterLayout() call in FrameView::layout() does not? Your changelog needs to convince me that you understand the underlying cause for this bug. This feels like wallpapering over the cracks.
sugoi
Comment 25
2013-01-31 06:22:36 PST
Created
attachment 185769
[details]
Patch
sugoi
Comment 26
2013-01-31 06:28:01 PST
Comment on
attachment 185769
[details]
Patch View in context:
https://bugs.webkit.org/attachment.cgi?id=185769&action=review
> Source/WebCore/rendering/RenderLayer.cpp:380 > + if (RenderLayer* compLayer = enclosingCompositingLayer()) {
@smfr: You were right, the enclosingCompositingLayer() function returns the proper layer, no need to use the root layer directly. They were probably the same in my test cases, which is why these cases required the root layer (also, I didn't know about the enclosingCompositingLayer() function), but this code also fixes the issue.
Build Bot
Comment 27
2013-01-31 08:11:16 PST
Comment on
attachment 185769
[details]
Patch
Attachment 185769
[details]
did not pass mac-wk2-ews (mac-wk2): Output:
http://queues.webkit.org/results/16251416
New failing tests: fast/canvas/canvas-overflow-hidden-animation.html
Simon Fraser (smfr)
Comment 28
2013-01-31 09:12:16 PST
Comment on
attachment 185769
[details]
Patch View in context:
https://bugs.webkit.org/attachment.cgi?id=185769&action=review
> Source/WebCore/rendering/RenderLayer.cpp:390 > +#if USE(ACCELERATED_COMPOSITING) > + if ((flags & UpdateCompositingLayers) && (rootLayer != this)) { > + if (RenderLayer* compLayer = enclosingCompositingLayer()) { > + RenderLayerBacking::UpdateAfterLayoutFlags updateFlags = > + RenderLayerBacking::CompositingChildrenOnly; > + if (flags & NeedsFullRepaintInBacking) > + updateFlags |= RenderLayerBacking::NeedsFullRepaint; > + if (flags & IsCompositingUpdateRoot) > + updateFlags |= RenderLayerBacking::IsUpdateRoot; > + compLayer->backing()->updateAfterLayout(updateFlags); > + } > + } > +#endif
Does it not work just to start the updateLayerPositions() at the enclosingCompositingLayer()? I think add new updateAfterLayout() calls here is going to result in extra work in many more cases.
sugoi
Comment 29
2013-01-31 10:06:16 PST
(In reply to
comment #28
)
> (From update of
attachment 185769
[details]
) > View in context:
https://bugs.webkit.org/attachment.cgi?id=185769&action=review
> > > Source/WebCore/rendering/RenderLayer.cpp:390 > > +#if USE(ACCELERATED_COMPOSITING) > > + if ((flags & UpdateCompositingLayers) && (rootLayer != this)) { > > + if (RenderLayer* compLayer = enclosingCompositingLayer()) { > > + RenderLayerBacking::UpdateAfterLayoutFlags updateFlags = > > + RenderLayerBacking::CompositingChildrenOnly; > > + if (flags & NeedsFullRepaintInBacking) > > + updateFlags |= RenderLayerBacking::NeedsFullRepaint; > > + if (flags & IsCompositingUpdateRoot) > > + updateFlags |= RenderLayerBacking::IsUpdateRoot; > > + compLayer->backing()->updateAfterLayout(updateFlags); > > + } > > + } > > +#endif > > Does it not work just to start the updateLayerPositions() at the enclosingCompositingLayer()? I think add new updateAfterLayout() calls here is going to result in extra work in many more cases.
Sure, it should work, but since this added code is called within updateLayerPositions(), around line 463, I thought it would be less costly to just call these lines rather than the whole function. I can easily change it to call the whole function, but I don't fully understand how that would affect performance positively.
WebKit Review Bot
Comment 30
2013-01-31 11:24:50 PST
Comment on
attachment 185769
[details]
Patch
Attachment 185769
[details]
did not pass chromium-ews (chromium-xvfb): Output:
http://queues.webkit.org/results/16273326
New failing tests: platform/chromium/virtual/gpu/fast/canvas/canvas-overflow-hidden-animation.html
sugoi
Comment 31
2013-01-31 11:52:41 PST
I redid my tests after seeing the failures and I made a mistake while doing my last tests. The current patch (using enclosingCompositingLayer()) doesn't work. Only the 4th patch, using the root layer, works, so it seems I'm back to patch number 4 as the latest working solution.
WebKit Review Bot
Comment 32
2013-01-31 12:05:14 PST
Comment on
attachment 185769
[details]
Patch
Attachment 185769
[details]
did not pass chromium-ews (chromium-xvfb): Output:
http://queues.webkit.org/results/16269354
New failing tests: platform/chromium/virtual/gpu/fast/canvas/canvas-overflow-hidden-animation.html
sugoi
Comment 33
2013-01-31 13:12:42 PST
Created
attachment 185846
[details]
Patch
sugoi
Comment 34
2013-01-31 13:18:11 PST
This new patch only does the missing geometry update for the current compositor, only for composited children and nothing else, so it may be a bit more efficient then the previous patch, and this also fixes the issue.
sugoi
Comment 35
2013-02-13 14:13:40 PST
Created
attachment 188179
[details]
Patch
sugoi
Comment 36
2013-02-13 14:22:01 PST
I found a way to fix the bug using the enclosingStackingContainer(). The issue was that, even if we would use the enclosingStackingContainer() as the root of the update inside RenderLayer, if that container isn't itself composited, it wouldn't trigger any updates in the accelerated compositing section, which is why the original solution using enclosingStackingContainer() failed. We do need to update from the enclosingStackingContainer() because only a stacking container can update the Z Order lists inside RenderLayerCompositor::updateCompositingDescendantGeometry() and these need to be updated, even on a subtree relayout. The current fix should hopefully be less costly than the original proposition, but still do the proper updates.
Simon Fraser (smfr)
Comment 37
2013-02-21 14:04:10 PST
Comment on
attachment 188179
[details]
Patch View in context:
https://bugs.webkit.org/attachment.cgi?id=188179&action=review
> Source/WebCore/rendering/RenderLayerBacking.cpp:465 > if (flags & IsUpdateRoot) { > updateGraphicsLayerGeometry(); > layerCompositor->updateRootLayerPosition(); > + RenderLayer* stackingContainer = m_owningLayer->enclosingStackingContainer(); > + if (!layerCompositor->compositingLayersNeedRebuild() && stackingContainer && (stackingContainer != m_owningLayer)) > + layerCompositor->updateCompositingDescendantGeometry(stackingContainer, stackingContainer, flags & CompositingChildrenOnly);
So for the root layer, this will do both the updateCompositingDescendantGeometry() a few lines above, and then do another update from the SC ancestor?
sugoi
Comment 38
2013-02-21 14:08:11 PST
> So for the root layer, this will do both the updateCompositingDescendantGeometry() a few lines above, and then do another update from the SC ancestor?
I don't think so, it won't do anything if stackingContainer == m_owningLayer, so if m_owningLayer is root, then its stacking container should either be itself or NULL (I don't know if NULL is possible for the root layer).
sugoi
Comment 39
2013-02-21 14:11:36 PST
(In reply to
comment #38
)
> > So for the root layer, this will do both the updateCompositingDescendantGeometry() a few lines above, and then do another update from the SC ancestor? > > I don't think so, it won't do anything if stackingContainer == m_owningLayer, so if m_owningLayer is root, then its stacking container should either be itself or NULL (I don't know if NULL is possible for the root layer).
But if you meant root as in m_owningLayer which is not the root layer, then yes, what you said is correct. Note that this is already the case as this function is called recursively from leaves to root, so updateCompositingDescendantGeometry() is already called in that fashion (children first, parents afterwards).
sugoi
Comment 40
2013-02-21 14:14:19 PST
What I'm describing is from the line : backing()->updateAfterLayout(updateFlags); (approximately line 464 in file RenderLayer.cpp)
sugoi
Comment 41
2013-03-04 13:13:50 PST
Friendly ping to reviewers. There's been no activity on this cl for more than a week, now. It's only 3 lines of code :)
Eric Seidel (no email)
Comment 42
2013-03-04 13:16:06 PST
jamesr or smfr are your best reviewers. I would need someone to bring me up to speed on current RenderLayer thinking before reviewing.
Simon Fraser (smfr)
Comment 43
2013-03-04 13:47:28 PST
Comment on
attachment 188179
[details]
Patch View in context:
https://bugs.webkit.org/attachment.cgi?id=188179&action=review
>> Source/WebCore/rendering/RenderLayerBacking.cpp:465 >> + layerCompositor->updateCompositingDescendantGeometry(stackingContainer, stackingContainer, flags & CompositingChildrenOnly); > > So for the root layer, this will do both the updateCompositingDescendantGeometry() a few lines above, and then do another update from the SC ancestor?
Actually I think this is fine.
WebKit Review Bot
Comment 44
2013-03-04 14:18:48 PST
Comment on
attachment 188179
[details]
Patch Clearing flags on attachment: 188179 Committed
r144674
: <
http://trac.webkit.org/changeset/144674
>
WebKit Review Bot
Comment 45
2013-03-04 14:18:55 PST
All reviewed patches have been landed. Closing bug.
Note
You need to
log in
before you can comment on or make changes to this bug.
Top of Page
Format For Printing
XML
Clone This Bug