will-change makes painting several orders of magnitude slower
Categories
(Core :: Graphics, defect, P3)
Tracking
()
People
(Reporter: kennylevinsen, Assigned: mattwoodrow)
References
Details
Attachments
(4 files, 1 obsolete file)
User Agent: Mozilla/5.0 (X11; Linux x86_64; rv:67.0) Gecko/20100101 Firefox/67.0
Steps to reproduce:
Material Design Components for Web (https://github.com/material-components/material-components-web) is very fond of "will-change: transform,opacity;", and has it set on simple things like their MDCButton (supposedly to warn the browser of the "ripple" effect that will come).
I observed that a page with a relatively low amount of buttons (thus will-change: opacity elements), in the ballpark of 60 buttons, would cause painting to come grinding to a halt.
With the console closed, paint time appears to be in the order of several seconds. However, a peculiar observation is that opening the console makes the problem much milder, which is of course unfortunate for performance tracing. For this particular site, a performance trace shows up to 900ms to paint.
Without will-change, painting only takes a few milliseconds.
While my minimal reproduction is attached. It is not quite as intense as the real page, but it shows the difference very clearly.
- Create a table with 10 cols by 50 rows
- Observe time spent painting on load
- Set will-change: opacity
- Observe time spent painting on load
Actual results:
The first test without will-change spends about 40ms in total painting on my small laptop, where the second test with will-change spends around 350ms painting.
Expected results:
Both tests should have spent a similar time painting.
Reporter | ||
Comment 1•5 years ago
|
||
Reporter | ||
Comment 2•5 years ago
|
||
Reporter | ||
Comment 3•5 years ago
|
||
Attached performance traces for the reproduction with/without will-change.
Comment 4•5 years ago
|
||
I am attempting to confirm this bug, but I seem to be unable to reproduce it. The test was done on Linux 16.04 LTS and Nightly v68.0a1.
Firstly, Opening the attachments from comment 1 and 2, I was expecting to see a different loading time between the two.
Furthermore, I have to mention that I do not see a will-change parameter in the Inspector tab of these 2 test pages.
Are the test pages correct? Am I reproducing it correctly?
Please retest in latest Nightly and explain exactly what I should do to reproduce it.
Thank you for your contribution!
Reporter | ||
Comment 5•5 years ago
|
||
I looked into the issue again, and managed to reproduce the full slow-down from the original site.
While will-change: opacity cause a x5-x10 increase in paint time (hundreds of ms instead of tens of ms), I found that the majority of the issue was in will-change: transform.
I found the 4 CSS styles that caused the several-second paint from the original page:
.test-table {
overflow-x: auto;
border-radius: 4px;
}
.mdc-button {
text-transform: uppercase;
will-change: transform;
}
Removing any of these styles fix the issue. Create a elements with the .mdc-button style inside a div with the test-table style, and the entire browser UI will freeze for several seconds on scroll.
Verified in 65 and 67a.01. Full reproduction HTML attached.
Reporter | ||
Comment 6•5 years ago
|
||
As for v68.0a1, I have the following observations:
- The problem persists with webrender disabled.
- The problem goes away with webrender enabled. This is new for v68, as the problem was present regardless of webrender in v67.
Reporter | ||
Comment 7•5 years ago
|
||
Just to clarify reproduction:
- Load the new reproduction HTML in Firefox nightly v68.0a1 with webrender disabled (default for most configurations).
- Scroll. The entire browser UI freezes for several seconds. On my machine, scroll takes > 4 seconds to complete, during which only a 2 frames are rendered (a small movement, and the final position).
With regards to the original reproduction which focused on the opacity part of the observed performance degradation: The paint-time appears to only differ in the sub-10ms region on v68.0a1, with and without webrender. This is not enough for me to be able to distinguish it from noise on my system, so I think that only the transform part of this degradation should be persued.
Reporter | ||
Updated•5 years ago
|
Comment 8•5 years ago
|
||
With the newly attached test case in comment 5, the issue will reproduce: a considerably slow load/paint/render can be noticed on Linux machines with all Firefox versions.
Assignee | ||
Comment 9•5 years ago
|
||
will-change:transform is forcing each button to be it's own Layer, and then we have to get the compositor to clip those Layers by the rounded rect border-radius clip from 'test-table'.
The resulting Layer tree contains a large number of repeats of this:
ContainerLayerComposite (0x167b1c400) [shadow-clip=(x=16, y=0, w=2498, h=1616)] [shadow-transform=[ 1 0; 0 1; 16 2877.13; ]] [clip=(x=16, y=16, w=2498, h=3110)] [transform=[ 1 0; 0 1; 16 2938; ]] [effective-transform=[ 1 0; 0 1; 16 3025; ]] [visible=< (x=-14, y=-14, w=180, h=63); >] [componentAlpha] [mMaskLayer=1402f3800] [metrics0={ [metrics={ [cb=(x=0.000000, y=0.000000, w=2530.000000, h=1616.000000)] [sr=(x=0.000000, y=0.000000, w=1265.000000, h=1571.000000)] [s=(0,0)] [dp=(x=0.000000, y=0.000000, w=1265.000000, h=1571.000000)] [cdp=(x=0.000000, y=0.000000, w=0.000000, h=0.000000)] [scrollId=2] [rcd] [z=2] }] [color=rgba(255, 255, 255, 1.000000)] [clip=(x=0, y=0, w=2530, h=1616)] }] [usesTmpSurf] [presShellResolution=1]
Mask layer:
ImageLayerComposite (0x1402f3800) [shadow-transform=[ 1 0; 0 1; 16 -44.8667; ]] [transform=[ 1 -0; -0 1; 16 16; ]] [effective-transform=[ 1 0; 0 1; 16 103.133; ]] [not visible]
ImageHost (0x1288c4700)
ShmemTextureHost (0x167bbf5b0) [size=(w=2498, h=3110)] [format=SurfaceFormat::A8] [flags=TextureFlags::DISALLOW_BIGIMAGE] [picture-rect=(x=0, y=0, w=2498, h=3110)]
PaintedLayerComposite (0x1402f3000) [visible=< (x=-14, y=-14, w=180, h=63); >] { hitregion=< (x=0, y=0, w=152, h=35); >} [componentAlpha] [valid=< (x=-15, y=-15, w=182, h=65); >]
TiledContentHost (0x13ccdf4c0)
That's a relatively small layer (180x63), being masked by an enormous mask (2498x3110).
The performance problem only happens when we have a software compositor (the default for Linux), and these mask operations are being done by skia.
Looking at DrawTargetSkia::MaskSurface [1], it looks like we do a draw with the entire mask as the image, and use the source layer as the shader. That seems like it would touch too many pixels, except that the caller has already clipped to the draw rect [2] (confirmed with a debugger that the pushed clip is 180x63).
Lee, do you think you could take a look at why skia is so slow at this please?
[1] https://searchfox.org/mozilla-central/rev/dd7e27f4a805e4115d0dbee70e1220b23b23c567/gfx/2d/DrawTargetSkia.cpp#1451
[2] https://searchfox.org/mozilla-central/source/gfx/layers/basic/BasicLayersImpl.cpp#107
Assignee | ||
Comment 10•5 years ago
|
||
As a side note, using alpha image masks to represent rounded rect clipping doesn't seem ideal for a software compositor. Ideally we'd just be sharing the rounded rect information, and clipping to that directly. That's a much more involved project, working on code that we're trying to deprecate.
Updated•5 years ago
|
Assignee | ||
Comment 11•5 years ago
|
||
Performance profile: https://perfht.ml/2KnIhTM
Assignee | ||
Comment 12•5 years ago
|
||
Comment 13•5 years ago
|
||
Pushed by mwoodrow@mozilla.com: https://hg.mozilla.org/integration/autoland/rev/f704b21e1ab5 Apply snapping to mask Layer transforms like we do for other Layer types. r=lsalzman
Comment 14•5 years ago
|
||
bugherder |
Updated•5 years ago
|
Updated•5 years ago
|
Updated•5 years ago
|
Updated•5 years ago
|
Comment 15•5 years ago
|
||
I have verified this fix in Nightly v68.0a1 (2019-05-15) (64-bit). It still occurs on Beta v67.0 (64-bit) release candidate.
Description
•