Closed Bug 1606685 Opened 6 months ago Closed 6 months ago

Transparent slices with "gaps" are too expensive

Categories

(Core :: Graphics: WebRender, defect, P3)

defect

Tracking

()

RESOLVED FIXED
mozilla74
Tracking Status
firefox73 --- wontfix
firefox74 --- fixed

People

(Reporter: mstange, Assigned: gw)

References

Details

Attachments

(4 files)

At the moment, transparent slices have a cost that's based on the size of their bounding box. Extra cost from transparent parts in the bounding box can easily tip the scales such that not creating a slice (and drawing the items into an existing slice) would have been cheaper.

For example, if you have two 10x10 items in two opposite corners of a 1000x1000 area, this slice adds 1000x1000 pixels of OS compositor overdraw to the window. Only 2x10x10 of that overdraw is useful, the rest is wasted memory bandwidth.
Let's say these two items are located in different tiles, and there's an entire empty tile between them. Now there are two things we can do to reduce OS compositor overdraw:

  1. We can discard tiles in the middle that are entirely empty.
  2. For the tiles that contain content, we can compute a separate clip per tile, so that the empty padding around the content of those tiles can be clipped away and no longer impacts OS compositor cost.

We were doing 1 up until bug 1604827. We have never done 2 - the clip has always been per-slice (i.e. shared between all tiles of a slice) and this has always caused unnecessary overdraw cost.

We need to bring back the ability to do 1. If we want to do 2 separately, we can file a new bug for it.

I'm going to attach a few screenshots with large red areas (from gfx.core-animation.tint-opaque) which indicate unnecessary OS compositor overdraw from transparent gaps.

80% of the browser window is overlaid with transparent pixels from the slice that contains the URL bar at the top and the status bar at the bottom.

The two scrollbars are assigned to the same slice (bug?) and make a large red bounding box.

Priority: -- → P3

Example of layerization trade-offs with fixed element + scrolling

In this example I'd like to compute the "cost" of a scroll on a page with scrolled content and a fixed element.

We estimate the cost of a frame as rasterization cost + compositing cost. We express these costs as "number of pixels drawn to", as percentages of the content area.
Let's say the scrolled content has an overdraw factor of 1.3 and the fixed element is transparent and covers 20% of the content area and also has an internal overdraw of 1.3.

Case 1: One slice, scrolled

If we draw the fixed content into the opaque scrolled slice, we get the following costs:

Initial paint:

  rasterization cost + compositing cost
= 1.3 * 100% + 1.3 * 20% for rasterization + 100% for OS compositing of the opaque layer
= 130% + 26% + 100% = 256%

Scroll that doesn't need new tiles:

  rasterization cost + compositing cost
= 0% rasterization for the scrolled content that doesn't overlap the fixed element
   + 1.3 * 20% + 1.3 * 20% rasterization of the content that overlaps with the fixed element
   + 100% for OS compositing of the opaque layer
= 2 * 1.3 * 20% + 100% = 52% + 100% = 152%

Case 2: Two slices, one scrolled and one fixed

If we put the fixed content into a transparent fixed slice, we get the following costs:

Initial paint:

  rasterization cost + compositing cost
= 1.3 * 100% + 1.3 * 20% for rasterization
   + 100% for OS compositing of the opaque layer
   + 20% for OS compositing of the transparent layer
= 130% + 26% + 100% + 20% = 276%

Scroll that doesn't need new tiles:

  rasterization cost + compositing cost
= 0% rasterization for the scrolled content
   + 0% rasterization for the fixed content
   + 120% compositing
= 120%

Evaluation

One slice Two slices
Initial frame 256% 276%
Frame with scroll 152% 120%

So we pay 20% on the initial paint and save 32% per scroll. That would be worth doing!

Where do these 32% savings come from? They come from the fact that the additional compositor overdraw is less than the drawing overdraw. The "drawing overdraw" for the affected area is 2.6, and the additional "compositor overdraw" for the affected area is 1. (2.6 - 1) = 1.6, 1.6 * 20% = 32%.

So in this case, creating the slice is worth doing because 2.6 is more than 1, i.e. because the "drawing overdraw" for the area in the affected slices is larger than one.

The cost of transparent gaps

Now, what happens if we have gaps in the transparent slice? This reduces the "drawing overdraw" in the slice to something that's less than one! Let's say only 10% of the transparent slice's bounding box are covered with content, and 90% are empty. The "drawing overdraw" in those 10% is still 1.3 in our example. But the overall "drawing overdraw" in the slice is now 0.13. If we plug this number into the calculation above, we get:
(0.13 - 1) * 20% = -0.87 * 20% = -17.4%
In other words, having the separate slice increases the cost of a scroll, by a pixel count of 17.4% of the size of the content area.

As soon as the compositor needs to composite transparency, the chances that performance benefits from a separate slice go down rapidly. We should make layerization decisions in such a way that the compositor never has to touch pixels that regular drawing wouldn't touch.

I completely agree - I've noticed this myself.

I think the simplest way to solve this for now is to add a flag that is passed to the native compositor trait specifying if a tile is empty.

In this way, native compositors can:

  • If using a tile-based implementation (mac + current DC impl) - take advantage of this to skip tile creation / blending completely.
  • If using a virtual surface backed implementation, allocate the tile surface.

This still means paying the performance cost if using virtual surfaces, but is a quick fix / workaround for now while we work out a better long term solution. Does this seem reasonable?

Assignee: nobody → gwatson
Flags: needinfo?(mstange)

I think that doing option (2) [providing a per-tile clip rect] should be quite straightforward, now that I have thought about it a bit more.

I think this will effectively solve both issues, since an empty tile could be completely skipped by the empty clip rect.

I'm going to prototype this today.

(In reply to Glenn Watson [:gw] from comment #6)

I think that doing option (2) [providing a per-tile clip rect] should be quite straightforward, now that I have thought about it a bit more.

I think this will effectively solve both issues, since an empty tile could be completely skipped by the empty clip rect.

That sounds great!

Flags: needinfo?(mstange)

This adds support for holes within virtual surfaces. On platforms
that don't use virtual surfaces, this just works by destroying
the tile that is empty so it never gets composited.

This implements the first part - supporting empty tiles (holes) in surfaces. I'm working on the second part (per-tile clip rects to reduce pixel count further) as a follow up.

Pushed by gwatson@mozilla.com:
https://hg.mozilla.org/integration/autoland/rev/e9d191b5eb8a
Support empty tiles within compositor surfaces. r=sotaro
Status: NEW → RESOLVED
Closed: 6 months ago
Resolution: --- → FIXED
Target Milestone: --- → mozilla74
You need to log in before you can comment on or make changes to this bug.