Another long overdue newsletter is here (I still consistently underestimate the time it takes to gather information and write these up, oops!).

I have been using Firefox with WebRender enabled as my main browser for a little while now on various computers. It’s great to see that it actually works and on many pages outperforms Firefox without WebRender. Of course we are still hitting pages where WebRender doesn’t perform very well but these tend to highlight specific areas of WebRender with a naive implementation that hasn’t been optimized yet. It’s quite satisfying because these tend to be quickly fixed. We aren’t running into fundamental design flaws which is always a risk when rewriting such a large piece of tech from scratch.

There are some configurations with graphics drivers that play very well with WebRender (intel on linux and nvidia on windows have worked quite well for me so far) and others that don’t (I’ve had a lot of issues with the proprietary nvidia drivers on linux and whatever intel chip is on the low end surface tablets for example). Fine tuning WebRender to play nice with the most common hardware and driver configurations is going to be a big challenge that we’ll have to go through before shipping, and we aren’t quite there yet.

Before I go through some of the code changes, I want to give shout-out for Darkspirit who is helping a lot with testing and triaging on bugzilla and github. Thanks a lot!

Update – What does image.mem.shared do?

This is a popular question in the comments section of this post, let’s see:

Gecko has an internal representation of the page that we call the Display list. When WebRender is disabled we walk through this display list and each display item on the list knows how to paint itself into the destination surface on the content process. WebRender has its own display list format, which is a little different, and we have to turn the Gecko display list into a WebRender display list and send it to the GPU/compositor process where WebRender renders it.

At the early days of WebRender’s integration we started with a rather naïve transformation of the display list that would have each Gecko image display item create a WebRender image object, copy the decoded image into it and create a WebRender image display item that refers to it. This meant that if two display items were referring to the same image, they would each create their own WebRender copy of the image. Ouch! If you have a “sprite sheet” (a big image that has for example all of the icons in the page and many HTML elements refer to portions of that image) in a site, which is fairly common, this would go very wrong very quickly, because the sprite sheet would end up duplicated many times (lots of CPU time, and memory bandwidth spent copying data around, and a lot more memory used as well).

Andrew’s work, in a nutshell, made it so that we can decode the image in shared memory directly (removing an expensive copy) and have all image display items that are using the same image refer to that shared image object instead of creating their own copy (no more duplication). And this will soon be enabled by default but is currently behind the “image.mem.shared” pref. Note that we are (well, Andrew is, single-handedly) still in the process of getting SVG images to work well but it isn’t implemented yet (If you are wondering why your memory usage explodes when adding an emoji in mastdon for example, that’s what is happening, and will soon be fixed).

Notable WebRender changes

Glenn is incrementally refactoring WebRender’s frame building and batching architecture to better support segmenting primitives into parts and move as many pixels as possible to the opaque pass. This ongoing work is spread over many pull requests and has already yield great performance improvements.

Morris greatly improved the performance of blurs with large radii.

Nical fixed a bug in the rendering of dotted borders.

Gankro further improved serialization of glyphs.

Glenn removed the need to re-build scenes every frame during scrolling (this is a big CPU win).

Glenn implemented filtering out render tasks for filters that have no effect such as opacity(1.0).

Kvark made it possible for GPU queries to be toggled at runtime.

Nical improved the UI of the integrated GPU profiler a bit and exposed the settings to gecko.

Kvark investigated some of the remaining performance issues with motion mark.

Ethan fixed some issues related to pre-multiplied alpha and filters.

Kats fixed a hit testing bug.

Nical Implemented rendering common bullet points with webrender display items instead of blob images.

Kvark made the depth buffer optional in render passes to save memory bandwidth where it isn’t needed

Ethan fixed another rendering error with pre-multiplied alpha.

Kvark implemented support for the new document API on the renderer side.

Kvark worked around a GLSL compiler bug.

Kvark improved the logic that recycles render targets.

Notable Gecko changes

Kats completed the implementation of position:sticky.

A collection of motionmark work: jrmuizel landed a change to avoid invalidating blob images on tiny scale changes Kats fixed a bug which was taking about 19% of client side motionmark time

Lee optimized the way we send fonts to WR and eliminated font copying as a source of main thread jank.

Vincent fixed a crash.

Ethan fixed a bug that caused blob images to be missing.

Andrew landed scaled image container support which should reduce frequency of fallback rendering (See bugs 1183378, 1368776, 1366097.

Andrew made shared surfaces reuse the same image key across display items , need to set the pref “image.mem.shared” to true.

Andrew improved the performance of using SVGs as mask-image.

Jeff ensured we don’t fall back with -moz-border-*-colors on border sides that don’t have a border (the fallback was hitting us on gmail).

Sotaro improved the performance of recompiling shaders by caching the shader binary.

Ethan fixed a memory leak.

Lee implemented rendering pre-transformed glyphs in WebRender.

Sotaro removed a synchronization happening when submitting frames with ANGLE on Windows.

Morris and Sotaro fixed pipeline leaks.

Nical avoided using blob image serialization for content that must be panted on the content thread (such as native themed widgets).

Morris enabled WebRender support for filters (hue-rotate, opacity, saturate).

Kats Made APZ use WebRender’s hit testing code and later completed it to work with scroll bars and scroll thumbs.

Jerry integrated WebRender’s threads with gecko’s built-in profiler.

Markus improved the performance of rendering the title bar on mac.

Sotaro fixed some issues with google maps.

… and whole lot of other things as shown in the list of bugs closed since the previous newsletter.

Enabling WebRender in Firefox Nightly

In about:config:

– set “gfx.webrender.enabled” to true,

– set “gfx.webrender.blob-images” to true,

– set “image.mem.shared” to true,

– if you are on Linux, set “layers.acceleration.force-enabled” to true.

Note that WebRender can only be enabled in Firefox Nightly.