Bug 1379295 - Support vibrancy with webrender. draft
authorMarkus Stange <mstange@themasta.com>
Tue, 24 Oct 2017 23:27:48 -0400
changeset 685862 635644ab089ad1c857e892a4248b388aaff0ae41
parent 685861 0d19d2891a70e422da9f7e55b434453c76c223af
child 737234 4148e582c7f6e5922456eaf1501bc0eb6562ebcd
push id86022
push userbmo:mstange@themasta.com
push dateWed, 25 Oct 2017 03:29:34 +0000
bugs1379295
milestone58.0a1
Bug 1379295 - Support vibrancy with webrender. MozReview-Commit-ID: ErcG9rnSdw7
gfx/webrender/src/border.rs
gfx/webrender/src/device.rs
gfx/webrender/src/frame.rs
gfx/webrender/src/frame_builder.rs
gfx/webrender/src/prim_store.rs
gfx/webrender/src/render_backend.rs
gfx/webrender/src/renderer.rs
gfx/webrender/src/tiling.rs
gfx/webrender_api/src/display_item.rs
gfx/webrender_api/src/display_list.rs
gfx/webrender_bindings/WebRenderAPI.cpp
gfx/webrender_bindings/WebRenderAPI.h
gfx/webrender_bindings/src/bindings.rs
gfx/webrender_bindings/webrender_ffi_generated.h
layout/painting/nsDisplayList.cpp
layout/painting/nsDisplayList.h
--- a/gfx/webrender/src/border.rs
+++ b/gfx/webrender/src/border.rs
@@ -3,17 +3,17 @@
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 use api::{BorderSide, BorderStyle, BorderWidths, ClipAndScrollInfo, ColorF, LayerPoint, LayerRect};
 use api::{LayerPrimitiveInfo, LayerSize, NormalBorder, RepeatMode};
 use clip::ClipSource;
 use ellipse::Ellipse;
 use frame_builder::FrameBuilder;
 use gpu_cache::GpuDataRequest;
-use prim_store::{BorderPrimitiveCpu, PrimitiveContainer, TexelRect};
+use prim_store::{BorderPrimitiveCpu, FillOrClear, PrimitiveContainer, TexelRect};
 use tiling::PrimitiveFlags;
 use util::{lerp, pack_as_float};
 
 #[repr(u8)]
 #[derive(Debug, Copy, Clone, PartialEq)]
 pub enum BorderCornerInstance {
     None,
     Single, // Single instance needed - corner styles are same or similar.
@@ -380,56 +380,56 @@ impl FrameBuilder {
 
             // Add a solid rectangle for each visible edge/corner combination.
             if top_edge == BorderEdgeKind::Solid {
                 let mut info = info.clone();
                 info.rect = LayerRect::new(p0, LayerSize::new(rect_width, top_len));
                 self.add_solid_rectangle(
                     clip_and_scroll,
                     &info,
-                    &border.top.color,
+                    &FillOrClear::Fill(border.top.color),
                     PrimitiveFlags::None,
                 );
             }
             if left_edge == BorderEdgeKind::Solid {
                 let mut info = info.clone();
                 info.rect = LayerRect::new(
                     LayerPoint::new(p0.x, p0.y + top_len),
                     LayerSize::new(left_len, rect_height - top_len - bottom_len),
                 );
                 self.add_solid_rectangle(
                     clip_and_scroll,
                     &info,
-                    &border.left.color,
+                    &FillOrClear::Fill(border.left.color),
                     PrimitiveFlags::None,
                 );
             }
             if right_edge == BorderEdgeKind::Solid {
                 let mut info = info.clone();
                 info.rect = LayerRect::new(
                     LayerPoint::new(p1.x - right_len, p0.y + top_len),
                     LayerSize::new(right_len, rect_height - top_len - bottom_len),
                 );
                 self.add_solid_rectangle(
                     clip_and_scroll,
                     &info,
-                    &border.right.color,
+                    &FillOrClear::Fill(border.right.color),
                     PrimitiveFlags::None,
                 );
             }
             if bottom_edge == BorderEdgeKind::Solid {
                 let mut info = info.clone();
                 info.rect = LayerRect::new(
                     LayerPoint::new(p0.x, p1.y - bottom_len),
                     LayerSize::new(rect_width, bottom_len),
                 );
                 self.add_solid_rectangle(
                     clip_and_scroll,
                     &info,
-                    &border.bottom.color,
+                    &FillOrClear::Fill(border.bottom.color),
                     PrimitiveFlags::None,
                 );
             }
         } else {
             // Create clip masks for border corners, if required.
             let mut extra_clips = Vec::new();
             let mut corner_instances = [BorderCornerInstance::Single; 4];
 
--- a/gfx/webrender/src/device.rs
+++ b/gfx/webrender/src/device.rs
@@ -1872,16 +1872,21 @@ impl Device {
         }
     }
 
     pub fn set_blend_mode_premultiplied_alpha(&self) {
         self.gl.blend_func(gl::ONE, gl::ONE_MINUS_SRC_ALPHA);
         self.gl.blend_equation(gl::FUNC_ADD);
     }
 
+    pub fn set_blend_mode_premultiplied_dest_out(&self) {
+        self.gl.blend_func(gl::ZERO, gl::ONE_MINUS_SRC_ALPHA);
+        self.gl.blend_equation(gl::FUNC_ADD);
+    }
+
     pub fn set_blend_mode_alpha(&self) {
         self.gl.blend_func_separate(
             gl::SRC_ALPHA,
             gl::ONE_MINUS_SRC_ALPHA,
             gl::ONE,
             gl::ONE_MINUS_SRC_ALPHA,
         );
         self.gl.blend_equation(gl::FUNC_ADD);
--- a/gfx/webrender/src/frame.rs
+++ b/gfx/webrender/src/frame.rs
@@ -11,16 +11,17 @@ use api::{LocalClip, PipelineId, ScrollC
 use api::{ScrollLocation, ScrollPolicy, ScrollSensitivity, SpecificDisplayItem, StackingContext};
 use api::{ClipMode, TileOffset, TransformStyle, WorldPoint};
 use clip::ClipRegion;
 use clip_scroll_tree::{ClipScrollTree, ScrollStates};
 use euclid::rect;
 use frame_builder::{FrameBuilder, FrameBuilderConfig};
 use gpu_cache::GpuCache;
 use internal_types::{FastHashMap, FastHashSet, RendererFrame};
+use prim_store::FillOrClear;
 use profiler::{GpuCacheProfileCounters, TextureCacheProfileCounters};
 use resource_cache::{ResourceCache, TiledImageMap};
 use scene::{Scene, StackingContextHelpers, ScenePipeline};
 use tiling::{CompositeOps, PrimitiveFlags};
 use util::{subtract_rect, ComplexClipRegionHelpers};
 
 #[derive(Copy, Clone, PartialEq, PartialOrd, Debug, Eq, Ord)]
 pub struct FrameId(pub u32);
@@ -515,21 +516,29 @@ impl Frame {
                     context,
                     &prim_info,
                     &info.color,
                     &clip_and_scroll,
                 ) {
                     context.builder.add_solid_rectangle(
                         clip_and_scroll,
                         &prim_info,
-                        &info.color,
+                        &FillOrClear::Fill(info.color),
                         PrimitiveFlags::None,
                     );
                 }
             }
+            SpecificDisplayItem::ClearRectangle => {
+                context.builder.add_solid_rectangle(
+                    clip_and_scroll,
+                    &prim_info,
+                    &FillOrClear::Clear,
+                    PrimitiveFlags::None,
+                );
+            }
             SpecificDisplayItem::Line(ref info) => {
                 let prim_info = LayerPrimitiveInfo {
                     rect: LayerRect::zero(),
                     local_clip: *item.local_clip(),
                     is_backface_visible: prim_info.is_backface_visible,
                     tag: prim_info.tag,
                 };
 
@@ -727,34 +736,34 @@ impl Frame {
         if context.scene.root_pipeline_id != Some(pipeline_id) {
             if let Some(pipeline) = context.scene.pipelines.get(&pipeline_id) {
                 if let Some(bg_color) = pipeline.background_color {
                     let root_bounds = LayerRect::new(LayerPoint::zero(), *content_size);
                     let info = LayerPrimitiveInfo::new(root_bounds);
                     context.builder.add_solid_rectangle(
                         ClipAndScrollInfo::simple(clip_id),
                         &info,
-                        &bg_color,
+                        &FillOrClear::Fill(bg_color),
                         PrimitiveFlags::None,
                     );
                 }
             }
         }
 
 
         self.flatten_items(traversal, pipeline_id, context, LayerVector2D::zero());
 
         if self.frame_builder_config.enable_scrollbars {
             let scrollbar_rect = LayerRect::new(LayerPoint::zero(), LayerSize::new(10.0, 70.0));
             let info = LayerPrimitiveInfo::new(scrollbar_rect);
 
             context.builder.add_solid_rectangle(
                 ClipAndScrollInfo::simple(clip_id),
                 &info,
-                &DEFAULT_SCROLLBAR_COLOR,
+                &FillOrClear::Fill(DEFAULT_SCROLLBAR_COLOR),
                 PrimitiveFlags::Scrollbar(self.clip_scroll_tree.topmost_scrolling_node_id(), 4.0),
             );
         }
 
         context.builder.pop_stacking_context();
     }
 
     fn flatten_items<'a>(
@@ -1174,24 +1183,24 @@ fn try_to_add_rectangle_splitting_on_cli
         local_clip: LocalClip::from(*info.local_clip.clip_rect()),
         is_backface_visible: info.is_backface_visible,
         tag: None,
     };
 
     context.builder.add_solid_rectangle(
         *clip_and_scroll,
         &prim_info,
-        color,
+        &FillOrClear::Fill(*color),
         PrimitiveFlags::None,
     );
 
     for clipped_rect in &clipped_rects {
         let mut info = info.clone();
         info.rect = *clipped_rect;
         context.builder.add_solid_rectangle(
             *clip_and_scroll,
             &info,
-            color,
+            &FillOrClear::Fill(*color),
             PrimitiveFlags::None,
         );
     }
     true
 }
--- a/gfx/webrender/src/frame_builder.rs
+++ b/gfx/webrender/src/frame_builder.rs
@@ -22,17 +22,17 @@ use frame::FrameId;
 use gpu_cache::GpuCache;
 use internal_types::{FastHashMap, FastHashSet, HardwareCompositeOp};
 use picture::{PicturePrimitive};
 use plane_split::{BspSplitter, Polygon, Splitter};
 use prim_store::{BrushPrimitive, TexelRect, YuvImagePrimitiveCpu};
 use prim_store::{GradientPrimitiveCpu, ImagePrimitiveCpu, LinePrimitive, PrimitiveKind};
 use prim_store::{PrimitiveContainer, PrimitiveIndex};
 use prim_store::{PrimitiveStore, RadialGradientPrimitiveCpu};
-use prim_store::{RectanglePrimitive, TextRunPrimitiveCpu};
+use prim_store::{FillOrClear, RectanglePrimitive, TextRunPrimitiveCpu};
 use profiler::{FrameProfileCounters, GpuCacheProfileCounters, TextureCacheProfileCounters};
 use render_task::{AlphaRenderItem, ClearMode, ClipChain, RenderTask, RenderTaskId, RenderTaskLocation};
 use render_task::RenderTaskTree;
 use resource_cache::ResourceCache;
 use scene::ScenePipeline;
 use std::{mem, usize, f32, i32};
 use tiling::{ClipScrollGroup, ClipScrollGroupIndex, CompositeOps, Frame};
 use tiling::{ContextIsolation, RenderTargetKind, StackingContextIndex};
@@ -610,27 +610,33 @@ impl FrameBuilder {
         mem::replace(&mut self.pending_shadow_contents, pending_primitives);
         mem::replace(&mut self.shadow_prim_stack, shadows);
     }
 
     pub fn add_solid_rectangle(
         &mut self,
         clip_and_scroll: ClipAndScrollInfo,
         info: &LayerPrimitiveInfo,
-        color: &ColorF,
+        operation: &FillOrClear,
         flags: PrimitiveFlags,
     ) {
-        let prim = RectanglePrimitive { color: *color };
-
-        // Don't add transparent rectangles to the draw list, but do consider them for hit
-        // testing. This allows specifying invisible hit testing areas.
-        if color.a == 0.0 {
-            self.add_primitive_to_hit_testing_list(info, clip_and_scroll);
-            return;
-        }
+        let prim = match operation {
+            &FillOrClear::Fill(ref color) => {
+                // Don't add transparent rectangles to the draw list, but do consider them for hit
+                // testing. This allows specifying invisible hit testing areas.
+                if color.a == 0.0 {
+                    self.add_primitive_to_hit_testing_list(info, clip_and_scroll);
+                    return;
+                }
+                RectanglePrimitive { operation: *operation }
+            }
+            &FillOrClear::Clear => {
+                RectanglePrimitive { operation: *operation }
+            }
+        };
 
         let prim_index = self.add_primitive(
             clip_and_scroll,
             info,
             Vec::new(),
             PrimitiveContainer::Rectangle(prim),
         );
 
@@ -1330,17 +1336,17 @@ impl FrameBuilder {
                 }
             };
 
             self.add_primitive(
                 clip_and_scroll,
                 &fast_info,
                 clips,
                 PrimitiveContainer::Rectangle(RectanglePrimitive {
-                    color: *color,
+                    operation: FillOrClear::Fill(*color),
                 }),
             );
         } else {
             let blur_offset = 2.0 * blur_radius;
             let mut extra_clips = vec![];
             let mut blur_regions = vec![];
 
             match clip_mode {
--- a/gfx/webrender/src/prim_store.rs
+++ b/gfx/webrender/src/prim_store.rs
@@ -133,39 +133,53 @@ impl GpuCacheAddress {
 #[derive(Debug)]
 pub struct PrimitiveMetadata {
     pub opacity: PrimitiveOpacity,
     pub clip_sources: ClipSourcesHandle,
     pub prim_kind: PrimitiveKind,
     pub cpu_prim_index: SpecificPrimitiveIndex,
     pub gpu_location: GpuCacheHandle,
     pub clip_task_id: Option<RenderTaskId>,
+    pub is_clear: bool,
 
     // TODO(gw): In the future, we should just pull these
     //           directly from the DL item, instead of
     //           storing them here.
     pub local_rect: LayerRect,
     pub local_clip_rect: LayerRect,
     pub is_backface_visible: bool,
     pub screen_rect: Option<DeviceIntRect>,
 
     /// A tag used to identify this primitive outside of WebRender. This is
     /// used for returning useful data during hit testing.
     pub tag: Option<ItemTag>,
 }
 
+#[derive(Debug,Clone,Copy)]
+pub enum FillOrClear {
+    Fill(ColorF),
+    Clear,
+}
+
 #[derive(Debug)]
-#[repr(C)]
 pub struct RectanglePrimitive {
-    pub color: ColorF,
+    pub operation: FillOrClear,
 }
 
 impl ToGpuBlocks for RectanglePrimitive {
     fn write_gpu_blocks(&self, mut request: GpuDataRequest) {
-        request.push(self.color);
+        match &self.operation {
+            &FillOrClear::Fill(ref color) => {
+                request.push(color.premultiplied());
+            }
+            &FillOrClear::Clear => {
+                // Opaque black with operator dest out
+                request.push(ColorF::new(0.0, 0.0, 0.0, 1.0));
+            }
+        }
     }
 }
 
 #[derive(Debug)]
 pub struct BrushPrimitive {
     pub clip_mode: ClipMode,
     pub radius: BorderRadius,
 }
@@ -862,24 +876,34 @@ impl PrimitiveStore {
             clip_task_id: None,
             local_rect: *local_rect,
             local_clip_rect: *local_clip_rect,
             is_backface_visible: is_backface_visible,
             screen_rect: None,
             tag,
 
             opacity: PrimitiveOpacity::translucent(),
+            is_clear: false,
             prim_kind: PrimitiveKind::Rectangle,
             cpu_prim_index: SpecificPrimitiveIndex(0),
         };
 
         let metadata = match container {
             PrimitiveContainer::Rectangle(rect) => {
+                let (opacity, is_clear) = match &rect.operation {
+                    &FillOrClear::Fill(ref color) => {
+                        (PrimitiveOpacity::from_alpha(color.a), false)
+                    }
+                    &FillOrClear::Clear => {
+                        (PrimitiveOpacity::opaque(), true)
+                    }
+                };
                 let metadata = PrimitiveMetadata {
-                    opacity: PrimitiveOpacity::from_alpha(rect.color.a),
+                    opacity,
+                    is_clear,
                     prim_kind: PrimitiveKind::Rectangle,
                     cpu_prim_index: SpecificPrimitiveIndex(self.cpu_rectangles.len()),
                     ..base_metadata
                 };
 
                 self.cpu_rectangles.push(rect);
 
                 metadata
--- a/gfx/webrender/src/render_backend.rs
+++ b/gfx/webrender/src/render_backend.rs
@@ -686,16 +686,17 @@ trait ToDebugString {
 #[cfg(feature = "debugger")]
 impl ToDebugString for SpecificDisplayItem {
     fn debug_string(&self) -> String {
         match *self {
             SpecificDisplayItem::Image(..) => String::from("image"),
             SpecificDisplayItem::YuvImage(..) => String::from("yuv_image"),
             SpecificDisplayItem::Text(..) => String::from("text"),
             SpecificDisplayItem::Rectangle(..) => String::from("rectangle"),
+            SpecificDisplayItem::ClearRectangle => String::from("clear_rectangle"),
             SpecificDisplayItem::Line(..) => String::from("line"),
             SpecificDisplayItem::Gradient(..) => String::from("gradient"),
             SpecificDisplayItem::RadialGradient(..) => String::from("radial_gradient"),
             SpecificDisplayItem::BoxShadow(..) => String::from("box_shadow"),
             SpecificDisplayItem::Border(..) => String::from("border"),
             SpecificDisplayItem::PushStackingContext(..) => String::from("push_stacking_context"),
             SpecificDisplayItem::Iframe(..) => String::from("iframe"),
             SpecificDisplayItem::Clip(..) => String::from("clip"),
--- a/gfx/webrender/src/renderer.rs
+++ b/gfx/webrender/src/renderer.rs
@@ -613,16 +613,17 @@ impl SourceTextureResolver {
     }
 }
 
 #[derive(Debug, Copy, Clone, PartialEq)]
 pub enum BlendMode {
     None,
     Alpha,
     PremultipliedAlpha,
+    PremultipliedDestOut,
     Subpixel,
 }
 
 // Tracks the state of each row in the GPU cache texture.
 struct CacheRow {
     is_dirty: bool,
 }
 
@@ -2345,16 +2346,17 @@ impl Renderer {
                 GPU_TAG_PRIM_BLEND
             }
             BatchKind::Transformable(transform_kind, batch_kind) => match batch_kind {
                 TransformBatchKind::Rectangle(needs_clipping) => {
                     debug_assert!(
                         !needs_clipping || match key.blend_mode {
                             BlendMode::Alpha |
                             BlendMode::PremultipliedAlpha |
+                            BlendMode::PremultipliedDestOut |
                             BlendMode::Subpixel => true,
                             BlendMode::None => false,
                         }
                     );
 
                     if needs_clipping {
                         self.ps_rectangle_clip.bind(
                             &mut self.device,
@@ -2710,16 +2712,17 @@ impl Renderer {
             self.gpu_profile.add_sampler(GPU_SAMPLER_TAG_TRANSPARENT);
 
             for batch in &target.alpha_batcher.batch_list.alpha_batch_list.batches {
                 if self.debug_flags.contains(DebugFlags::ALPHA_PRIM_DBG) {
                     let color = match batch.key.blend_mode {
                         BlendMode::None => ColorF::new(0.3, 0.3, 0.3, 1.0),
                         BlendMode::Alpha => ColorF::new(0.0, 0.9, 0.1, 1.0),
                         BlendMode::PremultipliedAlpha => ColorF::new(0.0, 0.3, 0.7, 1.0),
+                        BlendMode::PremultipliedDestOut => ColorF::new(0.6, 0.2, 0.0, 1.0),
                         BlendMode::Subpixel => ColorF::new(0.5, 0.0, 0.4, 1.0),
                     }.into();
                     for item_rect in &batch.item_rects {
                         self.debug.add_rect(item_rect, color);
                     }
                 }
 
                 match batch.key.kind {
@@ -2786,17 +2789,17 @@ impl Renderer {
 
                                 // When drawing the 2nd pass, we know that the VAO, textures etc
                                 // are all set up from the previous draw_instanced_batch call,
                                 // so just issue a draw call here to avoid re-uploading the
                                 // instances and re-binding textures etc.
                                 self.device
                                     .draw_indexed_triangles_instanced_u16(6, batch.instances.len() as i32);
                             }
-                            BlendMode::Alpha | BlendMode::None => {
+                            BlendMode::Alpha | BlendMode::PremultipliedDestOut | BlendMode::None => {
                                 unreachable!("bug: bad blend mode for text");
                             }
                         }
 
                         prev_blend_mode = BlendMode::None;
                         self.device.set_blend(false);
                     }
                     _ => {
@@ -2808,16 +2811,20 @@ impl Renderer {
                                 BlendMode::Alpha => {
                                     self.device.set_blend(true);
                                     self.device.set_blend_mode_alpha();
                                 }
                                 BlendMode::PremultipliedAlpha => {
                                     self.device.set_blend(true);
                                     self.device.set_blend_mode_premultiplied_alpha();
                                 }
+                                BlendMode::PremultipliedDestOut => {
+                                    self.device.set_blend(true);
+                                    self.device.set_blend_mode_premultiplied_dest_out();
+                                }
                                 BlendMode::Subpixel => {
                                     unreachable!("bug: subpx text handled earlier");
                                 }
                             }
                             prev_blend_mode = batch.key.blend_mode;
                         }
 
                         self.submit_batch(
--- a/gfx/webrender/src/tiling.rs
+++ b/gfx/webrender/src/tiling.rs
@@ -56,17 +56,24 @@ impl AlphaBatchHelpers for PrimitiveStor
             PrimitiveKind::TextRun => {
                 let text_run_cpu = &self.cpu_text_runs[metadata.cpu_prim_index.0];
                 match text_run_cpu.font.render_mode {
                     FontRenderMode::Subpixel => BlendMode::Subpixel,
                     FontRenderMode::Alpha |
                     FontRenderMode::Mono |
                     FontRenderMode::Bitmap => BlendMode::PremultipliedAlpha,
                 }
-            }
+            },
+            PrimitiveKind::Rectangle => if metadata.is_clear {
+                BlendMode::PremultipliedDestOut
+            } else if needs_blending {
+                BlendMode::PremultipliedAlpha
+            } else {
+                BlendMode::None
+            },
             PrimitiveKind::Image |
             PrimitiveKind::AlignedGradient |
             PrimitiveKind::AngleGradient |
             PrimitiveKind::RadialGradient |
             PrimitiveKind::Picture => if needs_blending {
                 BlendMode::PremultipliedAlpha
             } else {
                 BlendMode::None
@@ -248,17 +255,18 @@ impl BatchList {
 
     fn get_suitable_batch(
         &mut self,
         key: BatchKey,
         item_bounding_rect: &DeviceIntRect,
     ) -> &mut Vec<PrimitiveInstance> {
         match key.blend_mode {
             BlendMode::None => self.opaque_batch_list.get_suitable_batch(key),
-            BlendMode::Alpha | BlendMode::PremultipliedAlpha | BlendMode::Subpixel => {
+            BlendMode::Alpha | BlendMode::PremultipliedAlpha |
+            BlendMode::PremultipliedDestOut | BlendMode::Subpixel => {
                 self.alpha_batch_list
                     .get_suitable_batch(key, item_bounding_rect)
             }
         }
     }
 
     fn finalize(&mut self) {
         self.opaque_batch_list.finalize()
--- a/gfx/webrender_api/src/display_item.rs
+++ b/gfx/webrender_api/src/display_item.rs
@@ -86,16 +86,17 @@ pub type LayoutPrimitiveInfo = Primitive
 pub type LayerPrimitiveInfo = PrimitiveInfo<LayerPixel>;
 
 #[derive(Clone, Copy, Debug, Deserialize, PartialEq, Serialize)]
 pub enum SpecificDisplayItem {
     Clip(ClipDisplayItem),
     ScrollFrame(ScrollFrameDisplayItem),
     StickyFrame(StickyFrameDisplayItem),
     Rectangle(RectangleDisplayItem),
+    ClearRectangle,
     Line(LineDisplayItem),
     Text(TextDisplayItem),
     Image(ImageDisplayItem),
     YuvImage(YuvImageDisplayItem),
     Border(BorderDisplayItem),
     BoxShadow(BoxShadowDisplayItem),
     Gradient(GradientDisplayItem),
     RadialGradient(RadialGradientDisplayItem),
--- a/gfx/webrender_api/src/display_list.rs
+++ b/gfx/webrender_api/src/display_list.rs
@@ -765,16 +765,20 @@ impl DisplayListBuilder {
         debug_assert_eq!(len, count);
     }
 
     pub fn push_rect(&mut self, info: &LayoutPrimitiveInfo, color: ColorF) {
         let item = SpecificDisplayItem::Rectangle(RectangleDisplayItem { color });
         self.push_item(item, info);
     }
 
+    pub fn push_clear_rect(&mut self, info: &LayoutPrimitiveInfo) {
+        self.push_item(SpecificDisplayItem::ClearRectangle, info);
+    }
+
     pub fn push_line(
         &mut self,
         info: &LayoutPrimitiveInfo,
         baseline: f32,
         start: f32,
         end: f32,
         orientation: LineOrientation,
         width: f32,
--- a/gfx/webrender_bindings/WebRenderAPI.cpp
+++ b/gfx/webrender_bindings/WebRenderAPI.cpp
@@ -836,16 +836,24 @@ DisplayListBuilder::PushRect(const wr::L
   WRDL_LOG("PushRect b=%s cl=%s c=%s\n", mWrState,
       Stringify(aBounds).c_str(),
       Stringify(aClip).c_str(),
       Stringify(aColor).c_str());
   wr_dp_push_rect(mWrState, aBounds, aClip, aIsBackfaceVisible, aColor);
 }
 
 void
+DisplayListBuilder::PushClearRect(const wr::LayoutRect& aBounds)
+{
+  WRDL_LOG("PushClearRect b=%s\n", mWrState,
+      Stringify(aBounds).c_str());
+  wr_dp_push_clear_rect(mWrState, aBounds);
+}
+
+void
 DisplayListBuilder::PushLinearGradient(const wr::LayoutRect& aBounds,
                                        const wr::LayoutRect& aClip,
                                        bool aIsBackfaceVisible,
                                        const wr::LayoutPoint& aStartPoint,
                                        const wr::LayoutPoint& aEndPoint,
                                        const nsTArray<wr::GradientStop>& aStops,
                                        wr::ExtendMode aExtendMode,
                                        const wr::LayoutSize aTileSize,
--- a/gfx/webrender_bindings/WebRenderAPI.h
+++ b/gfx/webrender_bindings/WebRenderAPI.h
@@ -253,16 +253,18 @@ public:
                              const wr::WrClipId* aClipId);
   void PopClipAndScrollInfo();
 
   void PushRect(const wr::LayoutRect& aBounds,
                 const wr::LayoutRect& aClip,
                 bool aIsBackfaceVisible,
                 const wr::ColorF& aColor);
 
+  void PushClearRect(const wr::LayoutRect& aBounds);
+
   void PushLinearGradient(const wr::LayoutRect& aBounds,
                           const wr::LayoutRect& aClip,
                           bool aIsBackfaceVisible,
                           const wr::LayoutPoint& aStartPoint,
                           const wr::LayoutPoint& aEndPoint,
                           const nsTArray<wr::GradientStop>& aStops,
                           wr::ExtendMode aExtendMode,
                           const wr::LayoutSize aTileSize,
--- a/gfx/webrender_bindings/src/bindings.rs
+++ b/gfx/webrender_bindings/src/bindings.rs
@@ -1345,16 +1345,25 @@ pub extern "C" fn wr_dp_push_rect(state:
 
     let mut prim_info = LayoutPrimitiveInfo::with_clip_rect(rect, clip.into());
     prim_info.is_backface_visible = is_backface_visible;
     state.frame_builder.dl_builder.push_rect(&prim_info,
                                              color);
 }
 
 #[no_mangle]
+pub extern "C" fn wr_dp_push_clear_rect(state: &mut WrState,
+                                        rect: LayoutRect) {
+    debug_assert!(unsafe { !is_in_render_thread() });
+
+    let prim_info = LayoutPrimitiveInfo::new(rect);
+    state.frame_builder.dl_builder.push_clear_rect(&prim_info);
+}
+
+#[no_mangle]
 pub extern "C" fn wr_dp_push_image(state: &mut WrState,
                                    bounds: LayoutRect,
                                    clip: LayoutRect,
                                    is_backface_visible: bool,
                                    stretch_size: LayoutSize,
                                    tile_spacing: LayoutSize,
                                    image_rendering: ImageRendering,
                                    key: WrImageKey) {
--- a/gfx/webrender_bindings/webrender_ffi_generated.h
+++ b/gfx/webrender_bindings/webrender_ffi_generated.h
@@ -1,13 +1,13 @@
 /* This Source Code Form is subject to the terms of the Mozilla Public
  * License, v. 2.0. If a copy of the MPL was not distributed with this
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
-/* Generated with cbindgen:0.1.26 */
+/* Generated with cbindgen:0.1.25 */
 
 /* DO NOT MODIFY THIS MANUALLY! This file was generated using cbindgen.
  * To generate this file:
  *   1. Get the latest cbindgen using `cargo install --force cbindgen`
  *      a. Alternatively, you can clone `https://github.com/rlhunt/cbindgen` and use a tagged release
  *   2. Run `rustup run nightly cbindgen toolkit/library/rust/ --crate webrender_bindings -o gfx/webrender_bindings/webrender_ffi_generated.h`
  */
 
@@ -274,17 +274,17 @@ struct TypedSize2D_f32__LayerPixel {
   }
 };
 
 typedef TypedSize2D_f32__LayerPixel LayerSize;
 
 typedef LayerSize LayoutSize;
 
 // Describes the memory layout of a display list.
-//
+// 
 // A display list consists of some number of display list items, followed by a number of display
 // items.
 struct BuiltDisplayListDescriptor {
   // The first IPC time stamp: before any work has been done
   uint64_t builder_start_time;
   // The second IPC time stamp: after serialization
   uint64_t builder_finish_time;
   // The third IPC time stamp: just before sending
@@ -314,27 +314,16 @@ struct WrOpacityProperty {
   float opacity;
 
   bool operator==(const WrOpacityProperty& aOther) const {
     return id == aOther.id &&
            opacity == aOther.opacity;
   }
 };
 
-// A 3d transform stored as a 4 by 4 matrix in row-major order in memory.
-//
-// Transforms can be parametrized over the source and destination units, to describe a
-// transformation from a space to another.
-// For example, `TypedTransform3D<f32, WordSpace, ScreenSpace>::transform_point3d`
-// takes a `TypedPoint3D<f32, WordSpace>` and returns a `TypedPoint3D<f32, ScreenSpace>`.
-//
-// Transforms expose a set of convenience methods for pre- and post-transformations.
-// A pre-transformation corresponds to adding an operation that is applied before
-// the rest of the transformation, while a post-transformation adds an operation
-// that is applied after.
 struct TypedTransform3D_f32__LayoutPixel__LayoutPixel {
   float m11;
   float m12;
   float m13;
   float m14;
   float m21;
   float m22;
   float m23;
@@ -390,34 +379,33 @@ struct IdNamespace {
   bool operator<=(const IdNamespace& aOther) const {
     return mHandle <= aOther.mHandle;
   }
 };
 
 typedef IdNamespace WrIdNamespace;
 
 // Represents RGBA screen colors with floating point numbers.
-//
+// 
 // All components must be between 0.0 and 1.0.
 // An alpha value of 1.0 is opaque while 0.0 is fully transparent.
 struct ColorF {
   float r;
   float g;
   float b;
   float a;
 
   bool operator==(const ColorF& aOther) const {
     return r == aOther.r &&
            g == aOther.g &&
            b == aOther.b &&
            a == aOther.a;
   }
 };
 
-// A 2d Point tagged with a unit.
 struct TypedPoint2D_f32__LayerPixel {
   float x;
   float y;
 
   bool operator==(const TypedPoint2D_f32__LayerPixel& aOther) const {
     return x == aOther.x &&
            y == aOther.y;
   }
@@ -577,17 +565,16 @@ struct NinePatchDescriptor {
 
   bool operator==(const NinePatchDescriptor& aOther) const {
     return width == aOther.width &&
            height == aOther.height &&
            slice == aOther.slice;
   }
 };
 
-// A 2d Vector tagged with a unit.
 struct TypedVector2D_f32__LayerPixel {
   float x;
   float y;
 
   bool operator==(const TypedVector2D_f32__LayerPixel& aOther) const {
     return x == aOther.x &&
            y == aOther.y;
   }
@@ -658,17 +645,16 @@ struct ByteSlice {
   size_t len;
 
   bool operator==(const ByteSlice& aOther) const {
     return buffer == aOther.buffer &&
            len == aOther.len;
   }
 };
 
-// A 2d Point tagged with a unit.
 struct TypedPoint2D_u16__Tiles {
   uint16_t x;
   uint16_t y;
 
   bool operator==(const TypedPoint2D_u16__Tiles& aOther) const {
     return x == aOther.x &&
            y == aOther.y;
   }
@@ -1053,16 +1039,21 @@ void wr_dp_push_box_shadow(WrState *aSta
                            ColorF aColor,
                            float aBlurRadius,
                            float aSpreadRadius,
                            float aBorderRadius,
                            BoxShadowClipMode aClipMode)
 WR_FUNC;
 
 WR_INLINE
+void wr_dp_push_clear_rect(WrState *aState,
+                           LayoutRect aRect)
+WR_FUNC;
+
+WR_INLINE
 void wr_dp_push_clip(WrState *aState,
                      uint64_t aClipId)
 WR_FUNC;
 
 WR_INLINE
 void wr_dp_push_clip_and_scroll_info(WrState *aState,
                                      uint64_t aScrollId,
                                      const uint64_t *aClipId)
--- a/layout/painting/nsDisplayList.cpp
+++ b/layout/painting/nsDisplayList.cpp
@@ -4649,16 +4649,32 @@ nsDisplayClearBackground::BuildLayer(nsD
   bool snap;
   nsRect bounds = GetBounds(aBuilder, &snap);
   int32_t appUnitsPerDevPixel = mFrame->PresContext()->AppUnitsPerDevPixel();
   layer->SetBounds(bounds.ToNearestPixels(appUnitsPerDevPixel)); // XXX Do we need to respect the parent layer's scale here?
 
   return layer.forget();
 }
 
+bool
+nsDisplayClearBackground::CreateWebRenderCommands(mozilla::wr::DisplayListBuilder& aBuilder,
+                                                  mozilla::wr::IpcResourceUpdateQueue& aResources,
+                                                  const StackingContextHelper& aSc,
+                                                  mozilla::layers::WebRenderLayerManager* aManager,
+                                                  nsDisplayListBuilder* aDisplayListBuilder)
+{
+  LayoutDeviceRect bounds = LayoutDeviceRect::FromAppUnits(
+    nsRect(ToReferenceFrame(), mFrame->GetSize()),
+    mFrame->PresContext()->AppUnitsPerDevPixel());
+
+  aBuilder.PushClearRect(aSc.ToRelativeLayoutRect(bounds));
+
+  return true;
+}
+
 nsRect
 nsDisplayOutline::GetBounds(nsDisplayListBuilder* aBuilder,
                             bool* aSnap) const
 {
   *aSnap = false;
   return mFrame->GetVisualOverflowRectRelativeToSelf() + ToReferenceFrame();
 }
 
--- a/layout/painting/nsDisplayList.h
+++ b/layout/painting/nsDisplayList.h
@@ -4090,16 +4090,22 @@ public:
   {
     return mozilla::LAYER_ACTIVE_FORCE;
   }
 
   virtual already_AddRefed<Layer> BuildLayer(nsDisplayListBuilder* aBuilder,
                                              LayerManager* aManager,
                                              const ContainerLayerParameters& aContainerParameters) override;
 
+  bool CreateWebRenderCommands(mozilla::wr::DisplayListBuilder& aBuilder,
+                               mozilla::wr::IpcResourceUpdateQueue& aResources,
+                               const StackingContextHelper& aSc,
+                               mozilla::layers::WebRenderLayerManager* aManager,
+                               nsDisplayListBuilder* aDisplayListBuilder) override;
+
   NS_DISPLAY_DECL_NAME("ClearBackground", TYPE_CLEAR_BACKGROUND)
 };
 
 /**
  * The standard display item to paint the outer CSS box-shadows of a frame.
  */
 class nsDisplayBoxShadowOuter final : public nsDisplayItem {
 public: