Closed Bug 1535159 Opened 8 months ago Closed 8 months ago

will-change makes painting several orders of magnitude slower

Categories

(Core :: Graphics, defect, P3)

67 Branch
All
Linux
defect

Tracking

()

VERIFIED FIXED
mozilla68
Tracking Status
firefox-esr60 --- wontfix
firefox66 --- wontfix
firefox67 --- wontfix
firefox68 --- verified

People

(Reporter: kennylevinsen, Assigned: mattwoodrow)

References

(Depends on 1 open bug)

Details

Attachments

(4 files, 1 obsolete file)

Attached file Reproduction example (obsolete) —

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.

  1. Create a table with 10 cols by 50 rows
  2. Observe time spent painting on load
  3. Set will-change: opacity
  4. 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.

Attached file Without will-change

Attached performance traces for the reproduction with/without will-change.

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!

Flags: needinfo?(bugzilla)

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.

Attachment #9050810 - Attachment is obsolete: true
Flags: needinfo?(bugzilla)

As for v68.0a1, I have the following observations:

  1. The problem persists with webrender disabled.
  2. The problem goes away with webrender enabled. This is new for v68, as the problem was present regardless of webrender in v67.

Just to clarify reproduction:

  1. Load the new reproduction HTML in Firefox nightly v68.0a1 with webrender disabled (default for most configurations).
  2. 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.

Summary: will-change: opacity makes painting orders of magnitude slower → will-change makes painting several orders of magnitude slower

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.

Status: UNCONFIRMED → NEW
Component: Untriaged → Web Painting
Ever confirmed: true
OS: Unspecified → Linux
Product: Firefox → Core
Hardware: Unspecified → All

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

Component: Web Painting → Graphics
Flags: needinfo?(lsalzman)

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.

Depends on: fixed-by-webrender
Priority: -- → P3

Performance profile: https://perfht.ml/2KnIhTM

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
Status: NEW → RESOLVED
Closed: 8 months ago
Resolution: --- → FIXED
Target Milestone: --- → mozilla68
Assignee: nobody → matt.woodrow
Depends on: 1543744
Flags: needinfo?(lsalzman)

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.

Status: RESOLVED → VERIFIED
You need to log in before you can comment on or make changes to this bug.