WebRender newsletter #34

By Nical

Happy new year! I’ll introduce WebRender’s 34th newsletter with a rather technical overview of a neat trick we call primitive segmentation. In previous posts I wrote about how we deal with batching and how we use the depth buffer both as a culling mechanism and as a way to save memory bandwidth. As a result, pixels rendered in the opaque pass are much cheaper than pixels rendered in the blend pass. This works great with rectangular opaque primitives that are axis-aligned so they don’t need anti-aliasing. Anti-aliasing, however, requires us to do some blending to smoothen the edges and rounded corners have some transparent pixels. We could tessellate a mesh that covers exactly the rounded primitive but we’d still need blending for the anti-aliasing of the border. What a shame, rounded corners are so common on the web, and they are often quite big.

Well, we don’t really need to render whole primitives at a time. for a transformed primitive we can always extract out the opaque part of the primitive and render the anti-aliased edges separately. Likewise, we can break rounded rectangles up into smaller opaque rectangles and the rectangles that contain the corners. We call this primitive segmentation and it helps at several levels: opaque segments can move to the opaque pass which means we get good memory bandwidth savings and better batching since batching complexity is mostly affected by the amount of work to perform during the blend pass. This also opens the door to interesting optimizations. For example we can break a primitive into segments, not only depending on the shapes of the primitive itself, but also on the shape of masks that are applied to it. This lets us create large rounded rectangle masks where only the rounded parts of the masks occupy significant amounts of space in the mask. More generally, there are a lot of complicated elements that can be reduced to simpler or more compact segments by applying the same family of tricks and render them as nine-patches or some more elaborate patchwork of segments (for example the box-shadow of a rectangle).

Segmented primitives

The way we represent this on the GPU is to pack all of the primitive descriptions in a large float texture. For each primitive we first pack the per-primitive data followed by the per-segment data. We dispatch instanced draw calls where each instance corresponds to a segment’s quad. The vertex shader finds all of the information it needs from the primitive offset and segment id of the quad it is working on.

The idea of breaking complicated primitives up into simpler segments isn’t new nor ground breaking, but I think that it is worth mentioning in the context of WebRender because of how well it integrates with the rest of our rendering architecture.

Notable WebRender and Gecko changes

  • Jeff fixed some issues with blob image recoordination.
  • Dan improved the primitive interning mechanism in WebRender.
  • Kats fixed a bug with position:sticky.
  • Kats fixed a memory leak.
  • Kats improved the CI.
  • Kvark fixed a crash caused by empty regions in the texture cache allocator.
  • Kvark fixed a division by zero in a shader.
  • Matt improved to the frame scheduling logic.
  • Matt fixed a hit-testing issue with opacity:0 divs.
  • Matt fixed a blob image validation issue.
  • Matt improved the performance of text DrawTargets.
  • Matt prevented opacity:0 animation from generating lots of CPU work.
  • Matt fixed a pixel snapping issue.
  • Matt reduced the number of YUV shader permutations.
  • Lee fixed a bug in the FreeType font backend that caused all sub-pixel AA text to be shifted by a pixel.
  • Lee implemented font variation on Linux.
  • Emilio fixed a clipping issue allowing web content to draw over the tab bar.
  • Emilio fixed a border rendering corruption.
  • Glenn added suport for picture caching when the content rect changes between display lists.
  • Glenn fixed some picture caching bugs (2, 3, 4, 5).
  • Glenn removed redundant clustering information.
  • Glenn fixed a clipping bug.
  • Sotaro and Bobby lazily iniztialized D3D devices.
  • Sotaro fixed a crash on Wayland.
  • Bobby improved memory usage.
  • Bobby improved some of the debugging facilities.
  • Bobby shrunk the size of some handles using NonZero.
  • Bobby improved the shader hashing speed to help startup.
  • Glenn fixed a picture caching bug with multiple scroll roots.
  • Glenn improved the performance of picture caching.
  • Glenn followed up with more picture caching improvements.

Ongoing work

The team is going through the remaining release blockers.

Enabling WebRender in Firefox Nightly

In about:config, set the pref “gfx.webrender.all” to true and restart the browser.

Reporting bugs

The best place to report bugs related to WebRender in Firefox is the Graphics :: WebRender component in bugzilla.

Note that it is possible to log in with a github account.