Bug 111627 - recalcStyle() with a Detach styleDiff calculates style twice
Summary: recalcStyle() with a Detach styleDiff calculates style twice
Status: RESOLVED INVALID
Alias: None
Product: WebKit
Classification: Unclassified
Component: Layout and Rendering (show other bugs)
Version: 528+ (Nightly build)
Hardware: Unspecified Unspecified
: P2 Normal
Assignee: Elliott Sprehn
URL:
Keywords:
Depends on: 113123 115042
Blocks:
  Show dependency treegraph
 
Reported: 2013-03-06 14:41 PST by Eric Seidel (no email)
Modified: 2014-04-10 22:10 PDT (History)
15 users (show)

See Also:


Attachments
Patch (4.41 KB, patch)
2013-03-06 16:51 PST, Elliott Sprehn
webkit.review.bot: commit-queue-
Details | Formatted Diff | Diff

Note You need to log in before you can comment on or make changes to this bug.
Description Eric Seidel (no email) 2013-03-06 14:41:21 PST
recalcStyle() with a Detach styleDiff calculates style twice

As noted in:
https://bugs.webkit.org/show_bug.cgi?id=111494#c16

This is extra-bad, because I believe using lazyAttach will always go down this code path.  Fixing this could be as much as an 8% win on html5-full-render, if it cuts our style-resolves in half. :)
Comment 1 Elliott Sprehn 2013-03-06 16:51:37 PST
Created attachment 191867 [details]
Patch
Comment 2 Eric Seidel (no email) 2013-03-06 16:53:52 PST
I'm not sure this is the solution we want to go with long-term. :)  But it would appear to solve the bug.

Do you have any perf data to show this is a win?
Comment 3 Ryosuke Niwa 2013-03-06 16:58:06 PST
There got to be a better way of doing this?
Comment 4 Elliott Sprehn 2013-03-06 16:59:26 PST
(In reply to comment #2)
> I'm not sure this is the solution we want to go with long-term. :)  But it would appear to solve the bug.
> 
> Do you have any perf data to show this is a win?

I'm having issues getting the perf tests to not hang after running them, but I did a few runs and it didn't seem like it was a win at all on html5-full-render.html
Comment 5 Elliott Sprehn 2013-03-06 17:01:38 PST
(In reply to comment #3)
> There got to be a better way of doing this?

It requires considerable amounts of plumbing since we'd need to pass the style from recalcStyle => reattach => attach => createRendererIfNeeded => NodeRenderingContext::createRendererForElementIfNeeded() which finally calls styleForRenderer().

I guess we could use the same class and use the style in createRendererForElementIfNeeded instead though which means StyleResolver doesn't need this knowledge, and it would work for PseudoElements too.
Comment 6 Antti Koivisto 2013-03-06 17:02:12 PST
Comment on attachment 191867 [details]
Patch

That's just too ugly. I have a patch somewhere that fixes this properly, by passing the style to attach().
Comment 7 Elliott Sprehn 2013-03-06 17:10:17 PST
Comment on attachment 191867 [details]
Patch

View in context: https://bugs.webkit.org/attachment.cgi?id=191867&action=review

> Source/WebCore/css/StyleResolver.cpp:1388
> +        return style;

If we did this approach, this code should be inside NodeRenderingContext::createRendererForElementIfNeeded
Comment 8 Elliott Sprehn 2013-03-06 17:36:08 PST
Ran perf tests for html5-full-render:

With this caching:
3202.66	± 0.23%
3205.94	± 0.17%

Without this caching:
3198.89	± 0.24%
3202.49	± 0.12%

So, surprisingly, it doesn't seem to really matter. I guess there's caching stuff down inside StyleResolver already?
Comment 9 WebKit Review Bot 2013-03-06 17:49:36 PST
Comment on attachment 191867 [details]
Patch

Attachment 191867 [details] did not pass chromium-ews (chromium-xvfb):
Output: http://webkit-commit-queue.appspot.com/results/17062114

New failing tests:
fast/css/pseudo-empty-dynamic-empty.html
fast/events/drag-display-none-element.html
fast/selectors/empty-element-made-non-empty.html
Comment 10 Build Bot 2013-03-06 17:56:05 PST
Comment on attachment 191867 [details]
Patch

Attachment 191867 [details] did not pass mac-wk2-ews (mac-wk2):
Output: http://webkit-commit-queue.appspot.com/results/17062117

New failing tests:
fast/css/pseudo-empty-dynamic-empty.html
http/tests/misc/acid3.html
fast/selectors/empty-element-made-non-empty.html
Comment 11 Allan Sandfeld Jensen 2013-03-07 05:34:57 PST
(In reply to comment #8)
> Ran perf tests for html5-full-render:
> 
> With this caching:
> 3202.66    ± 0.23%
> 3205.94    ± 0.17%
> 
> Without this caching:
> 3198.89    ± 0.24%
> 3202.49    ± 0.12%
> 
> So, surprisingly, it doesn't seem to really matter. I guess there's caching stuff down inside StyleResolver already?

It only caches in the sense that it will reuse the calculated style of a sibling if the elements are similar enough. To get a performance improvement you probably need to trigger a style that is unique.
Comment 12 Antti Koivisto 2013-03-07 11:14:23 PST
> So, surprisingly, it doesn't seem to really matter. I guess there's caching stuff down inside StyleResolver already?

StyleResolver has multiple levels of caches. Also the case just doesn't generally get hit that much. When reattach() gets called the whole subtree under the element will get attached outside recalcStyle without hitting the double calculation case. I have yet to see a real case where this is practical problem. It would still be nice to fix it as it is silly.
Comment 13 Eric Seidel (no email) 2013-03-07 13:01:06 PST
(In reply to comment #12)
> > So, surprisingly, it doesn't seem to really matter. I guess there's caching stuff down inside StyleResolver already?
> 
> Also the case just doesn't generally get hit that much. When reattach() gets called the whole subtree under the element will get attached outside recalcStyle without hitting the double calculation case.

Ah!  this is the key bit I was missing.  My understanding is lazyAttach() puts us into this state all the time.  But it doesn't end up showing up because we only double-style-calc for the root most element of a subtree.
Comment 14 Build Bot 2013-03-07 19:34:00 PST
Comment on attachment 191867 [details]
Patch

Attachment 191867 [details] did not pass mac-ews (mac):
Output: http://webkit-commit-queue.appspot.com/results/17081252

New failing tests:
fast/css/pseudo-empty-dynamic-empty.html
fast/events/drag-display-none-element.html
http/tests/misc/acid3.html
fast/selectors/empty-element-made-non-empty.html
Comment 15 Elliott Sprehn 2013-03-21 23:38:41 PDT
(In reply to comment #14)
> (From update of attachment 191867 [details])
> Attachment 191867 [details] did not pass mac-ews (mac):
> Output: http://webkit-commit-queue.appspot.com/results/17081252
> 
> New failing tests:
> fast/css/pseudo-empty-dynamic-empty.html
> fast/events/drag-display-none-element.html
> http/tests/misc/acid3.html
> fast/selectors/empty-element-made-non-empty.html

These failures have me pretty confused. Apparently when calling styleForRenderer() inside Element::recalcStyle we get a different style than we would when later calling it inside NodeRenderingContext.

Specifically:

style1 = styleForRenderer();
detach();
style2 = styleForRenderer();

These two styles don't have the same properties which breaks the idea of passing the style down through the reattach() call.

@antti Do you know what's going on here?
Comment 16 Elliott Sprehn 2013-03-22 01:16:38 PDT
(In reply to comment #15)
> (In reply to comment #14)
>  ...
> 
> These failures have me pretty confused. Apparently when calling styleForRenderer() inside Element::recalcStyle we get a different style than we would when later calling it inside NodeRenderingContext.
> 
> Specifically:
> 
> style1 = styleForRenderer();
> detach();
> style2 = styleForRenderer();
> 
> These two styles don't have the same properties which breaks the idea of passing the style down through the reattach() call.
> 
> @antti Do you know what's going on here?

Hmm, I'm wrong, there's something else weird going on here I'm missing. :/
Comment 17 Elliott Sprehn 2013-03-22 11:55:02 PDT
(In reply to comment #16)
> ...
> 
> Hmm, I'm wrong, there's something else weird going on here I'm missing. :/

Figured this out. We used to store the affectedBy bits in the RenderStyle, but now we store them in the ElementRareData and reset them in detach() which means calling styleForRenderer in recalcStyle sets a bunch of bits, but then detach() inside reattach() clears the affectedBy bits so we get a RenderStyle but none of the correct affectedBy bits. 

So we need to tell detach() not to clear the bits so we get the right values since we're not going to call styleForRenderer() again to set them.
Comment 18 Eric Seidel (no email) 2013-03-22 13:00:43 PDT
These are some of the side effects of style resolve which we'll need to make explicit. Dimitri and I were just musing about this yesterday.
Comment 19 Elliott Sprehn 2013-03-22 16:52:10 PDT
(In reply to comment #18)
> These are some of the side effects of style resolve which we'll need to make explicit. Dimitri and I were just musing about this yesterday.

Yeah. So not calling resetComputedStyle() inside detach() (from reattach) fixes some of the tests, but I'm still stuck for  LayoutTests/fast/events/drag-display-none-element.html

This test fails because the paragraph remains hidden. I tried not calling resetDynamicRestyleObservations but that doesn't seem to help.

I need to figure out what other shared state change happened inside style recalc.
Comment 20 Elliott Sprehn 2013-03-22 17:58:25 PDT
This would be resolved by passing the context in Bug 113123
Comment 21 Elliott Sprehn 2013-03-22 18:01:23 PDT
(In reply to comment #19)
> (In reply to comment #18)
> > These are some of the side effects of style resolve which we'll need to make explicit. Dimitri and I were just musing about this yesterday.
> 
> Yeah. So not calling resetComputedStyle() inside detach() (from reattach) fixes some of the tests, but I'm still stuck for  LayoutTests/fast/events/drag-display-none-element.html
> 
> This test fails because the paragraph remains hidden. I tried not calling resetDynamicRestyleObservations but that doesn't seem to help.
> 
> I need to figure out what other shared state change happened inside style recalc.

I figured this out btw, it's because SelectorChecker looks at the renderer for isDragging to set some state which is different after detach() since we lose the renderer. We should probably break that dependency.
Comment 22 Antti Koivisto 2013-04-23 09:24:20 PDT
http://trac.webkit.org/changeset/148970 fixed the most common instance of this problem.