Bug 1510082 - Update webrender to commit e2e52b1145ad959191c0612edd41b0b189cf6b59 (WR PR #3346). r=kats
authorWR Updater Bot <graphics-team@mozilla.staktrace.com>
Tue, 27 Nov 2018 02:52:50 +0000
changeset 504609 4585bb8bd85ac70f17a25bc2407d29b964a4e744
parent 504608 895fcf304c2a19969e8d075457a7cee36ea06895
child 504610 2b0c95030a8b9c00cd68bbb0c485b8378955ff40
push id10290
push userffxbld-merge
push dateMon, 03 Dec 2018 16:23:23 +0000
treeherdermozilla-beta@700bed2445e6 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewerskats
bugs1510082
milestone65.0a1
first release with
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
last release without
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
Bug 1510082 - Update webrender to commit e2e52b1145ad959191c0612edd41b0b189cf6b59 (WR PR #3346). r=kats https://github.com/servo/webrender/pull/3346 Differential Revision: https://phabricator.services.mozilla.com/D13026
gfx/webrender_bindings/revision.txt
gfx/wr/webrender/src/batch.rs
gfx/wr/webrender/src/box_shadow.rs
gfx/wr/webrender/src/display_list_flattener.rs
gfx/wr/webrender/src/frame_builder.rs
gfx/wr/webrender/src/picture.rs
gfx/wr/webrender/src/prim_store.rs
gfx/wr/webrender/src/storage.rs
gfx/wr/webrender/src/surface.rs
gfx/wr/wrench/reftests/boxshadow/inset-alpha.png
--- a/gfx/webrender_bindings/revision.txt
+++ b/gfx/webrender_bindings/revision.txt
@@ -1,1 +1,1 @@
-f450af9277e2474e2a2a2c1358689ca9486e2a09
+e2e52b1145ad959191c0612edd41b0b189cf6b59
--- a/gfx/wr/webrender/src/batch.rs
+++ b/gfx/wr/webrender/src/batch.rs
@@ -12,17 +12,17 @@ use gpu_cache::{GpuCache, GpuCacheHandle
 use gpu_types::{BrushFlags, BrushInstance, PrimitiveHeaders, ZBufferId, ZBufferIdGenerator};
 use gpu_types::{ClipMaskInstance, SplitCompositeInstance};
 use gpu_types::{PrimitiveInstanceData, RasterizationSpace, GlyphInstance};
 use gpu_types::{PrimitiveHeader, PrimitiveHeaderIndex, TransformPaletteId, TransformPalette};
 use internal_types::{FastHashMap, SavedTargetIndex, TextureSource};
 use picture::{Picture3DContext, PictureCompositeMode, PicturePrimitive, PictureSurface};
 use prim_store::{BrushKind, BrushPrimitive, DeferredResolve, PrimitiveTemplateKind};
 use prim_store::{EdgeAaSegmentMask, ImageSource, PrimitiveInstanceKind, PrimitiveStore};
-use prim_store::{VisibleGradientTile, PrimitiveInstance, PrimitiveOpacity};
+use prim_store::{VisibleGradientTile, PrimitiveInstance, PrimitiveOpacity, SegmentInstanceIndex};
 use prim_store::{BrushSegment, ClipMaskKind, ClipTaskIndex, PrimitiveDetails};
 use render_task::{RenderTaskAddress, RenderTaskId, RenderTaskTree};
 use renderer::{BlendMode, ImageBufferKind, ShaderColorMode};
 use renderer::BLOCKS_PER_UV_RECT;
 use resource_cache::{CacheItem, GlyphFetchResult, ImageRequest, ResourceCache, ImageProperties};
 use scene::FilterOpHelpers;
 use smallvec::SmallVec;
 use std::{f32, i32, usize};
@@ -900,16 +900,17 @@ impl AlphaBatchBuilder {
                             let prim_instance = &picture.prim_list.prim_instances[child.anchor];
                             let pic_index = match prim_instance.kind {
                                 PrimitiveInstanceKind::Picture { pic_index } => pic_index,
                                 PrimitiveInstanceKind::LineDecoration { .. } |
                                 PrimitiveInstanceKind::TextRun { .. } |
                                 PrimitiveInstanceKind::LegacyPrimitive { .. } |
                                 PrimitiveInstanceKind::NormalBorder { .. } |
                                 PrimitiveInstanceKind::ImageBorder { .. } |
+                                PrimitiveInstanceKind::Rectangle { .. } |
                                 PrimitiveInstanceKind::Clear => {
                                     unreachable!();
                                 }
                             };
                             let pic = &ctx.prim_store.pictures[pic_index.0];
 
                             // Get clip task, if set, for the picture primitive.
                             let clip_task_address = get_clip_task_address(
@@ -1612,16 +1613,81 @@ impl AlphaBatchBuilder {
                     bounding_rect,
                     transform_kind,
                     render_tasks,
                     z_id,
                     prim_instance.clip_task_index,
                     ctx,
                 );
             }
+            (
+                PrimitiveInstanceKind::Rectangle { segment_instance_index, opacity_binding_index, .. },
+                PrimitiveTemplateKind::Rectangle { .. }
+            ) => {
+                let specified_blend_mode = BlendMode::PremultipliedAlpha;
+                let opacity_binding = ctx.prim_store.get_opacity_binding(*opacity_binding_index);
+
+                let opacity = PrimitiveOpacity::from_alpha(opacity_binding);
+                let opacity = opacity.combine(prim_data.opacity);
+
+                let non_segmented_blend_mode = if !opacity.is_opaque ||
+                    prim_instance.clip_task_index != ClipTaskIndex::INVALID ||
+                    transform_kind == TransformedRectKind::Complex
+                {
+                    specified_blend_mode
+                } else {
+                    BlendMode::None
+                };
+
+                let batch_params = BrushBatchParameters::shared(
+                    BrushBatchKind::Solid,
+                    BatchTextures::no_texture(),
+                    [get_shader_opacity(opacity_binding), 0, 0],
+                    0,
+                );
+
+                let (prim_cache_address, segments) = if *segment_instance_index == SegmentInstanceIndex::UNUSED {
+                    (gpu_cache.get_address(&prim_data.gpu_cache_handle), None)
+                } else {
+                    let segment_instance = &ctx.scratch.segment_instances[*segment_instance_index];
+                    let segments = Some(&ctx.scratch.segments[segment_instance.segments_range]);
+                    (gpu_cache.get_address(&segment_instance.gpu_cache_handle), segments)
+                };
+
+                let prim_header = PrimitiveHeader {
+                    local_rect: prim_data.prim_rect,
+                    local_clip_rect: prim_instance.combined_local_clip_rect,
+                    task_address,
+                    specific_prim_address: prim_cache_address,
+                    clip_task_address,
+                    transform_id,
+                };
+
+                let prim_header_index = prim_headers.push(
+                    &prim_header,
+                    z_id,
+                    batch_params.prim_user_data,
+                );
+
+                self.add_segmented_prim_to_batch(
+                    segments,
+                    opacity,
+                    &batch_params,
+                    specified_blend_mode,
+                    non_segmented_blend_mode,
+                    prim_header_index,
+                    clip_task_address,
+                    bounding_rect,
+                    transform_kind,
+                    render_tasks,
+                    z_id,
+                    prim_instance.clip_task_index,
+                    ctx,
+                );
+            }
             _ => {
                 unreachable!();
             }
         }
     }
 
     fn add_image_tile_to_batch(
         &mut self,
@@ -1789,17 +1855,17 @@ impl AlphaBatchBuilder {
                 // No segments, and thus no per-segment instance data.
                 // Note: the blend mode already takes opacity into account
                 let batch_key = BatchKey {
                     blend_mode: non_segmented_blend_mode,
                     kind: BatchKind::Brush(params.batch_kind),
                     textures: segment_data.textures,
                 };
                 let instance = PrimitiveInstanceData::from(BrushInstance {
-                    segment_index: 0,
+                    segment_index: INVALID_SEGMENT_INDEX,
                     edge_flags: EdgeAaSegmentMask::all(),
                     clip_task_address,
                     brush_flags: BrushFlags::PERSPECTIVE_INTERPOLATION,
                     prim_header_index,
                     user_data: segment_data.user_data,
                 });
                 self.batch_list.push_single_instance(
                     batch_key,
@@ -1994,25 +2060,16 @@ impl BrushPrimitive {
                             ShaderColorMode::Image as i32 | ((alpha_type as i32) << 16),
                             RasterizationSpace::Local as i32,
                             get_shader_opacity(opacity_binding),
                         ],
                         cache_item.uv_rect_handle.as_int(gpu_cache),
                     ))
                 }
             }
-            BrushKind::Solid { opacity_binding_index, .. } => {
-                let opacity_binding = prim_store.get_opacity_binding(opacity_binding_index);
-                Some(BrushBatchParameters::shared(
-                    BrushBatchKind::Solid,
-                    BatchTextures::no_texture(),
-                    [get_shader_opacity(opacity_binding), 0, 0],
-                    0,
-                ))
-            }
             BrushKind::RadialGradient { ref stops_handle, .. } => {
                 Some(BrushBatchParameters::shared(
                     BrushBatchKind::RadialGradient,
                     BatchTextures::no_texture(),
                     [
                         stops_handle.as_int(gpu_cache),
                         0,
                         0,
@@ -2101,17 +2158,16 @@ impl PrimitiveInstance {
             PrimitiveDetails::Brush(ref brush) => {
                 match brush.kind {
                     BrushKind::Image { alpha_type, .. } => {
                         match alpha_type {
                             AlphaType::PremultipliedAlpha => BlendMode::PremultipliedAlpha,
                             AlphaType::Alpha => BlendMode::Alpha,
                         }
                     }
-                    BrushKind::Solid { .. } |
                     BrushKind::YuvImage { .. } |
                     BrushKind::RadialGradient { .. } |
                     BrushKind::LinearGradient { .. } => {
                         BlendMode::PremultipliedAlpha
                     }
                 }
             }
         }
--- a/gfx/wr/webrender/src/box_shadow.rs
+++ b/gfx/wr/webrender/src/box_shadow.rs
@@ -3,17 +3,17 @@
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 use api::{BorderRadius, BoxShadowClipMode, ClipMode, ColorF, DeviceIntSize, LayoutPrimitiveInfo};
 use api::{LayoutRect, LayoutSize, LayoutVector2D, MAX_BLUR_RADIUS};
 use clip::ClipItemKey;
 use display_list_flattener::DisplayListFlattener;
 use gpu_cache::GpuCacheHandle;
 use gpu_types::BoxShadowStretchMode;
-use prim_store::{BrushKind, BrushPrimitive, PrimitiveContainer};
+use prim_store::PrimitiveContainer;
 use prim_store::ScrollNodeAndClipChain;
 use render_task::RenderTaskCacheEntryHandle;
 use util::RectHelpers;
 
 #[derive(Debug, Clone)]
 #[cfg_attr(feature = "capture", derive(Serialize))]
 #[cfg_attr(feature = "replay", derive(Deserialize))]
 pub struct BoxShadowClipSource {
@@ -144,17 +144,19 @@ impl<'a> DisplayListFlattener<'a> {
                 clip_radius,
                 ClipMode::Clip,
             ));
 
             self.add_primitive(
                 clip_and_scroll,
                 &LayoutPrimitiveInfo::with_clip_rect(final_prim_rect, prim_info.clip_rect),
                 clips,
-                PrimitiveContainer::Brush(BrushPrimitive::new(BrushKind::new_solid(*color), None)),
+                PrimitiveContainer::Rectangle {
+                    color: *color,
+                },
             );
         } else {
             // Normal path for box-shadows with a valid blur radius.
             let blur_offset = BLUR_SAMPLE_SCALE * blur_radius;
             let mut extra_clips = vec![];
 
             // Add a normal clip mask to clip out the contents
             // of the surrounding primitive.
@@ -165,17 +167,19 @@ impl<'a> DisplayListFlattener<'a> {
             ));
 
             // Get the local rect of where the shadow will be drawn,
             // expanded to include room for the blurred region.
             let dest_rect = shadow_rect.inflate(blur_offset, blur_offset);
 
             // Draw the box-shadow as a solid rect, using a box-shadow
             // clip mask item.
-            let prim = BrushPrimitive::new(BrushKind::new_solid(*color), None);
+            let prim = PrimitiveContainer::Rectangle {
+                color: *color,
+            };
 
             // Create the box-shadow clip item.
             let shadow_clip_source = ClipItemKey::box_shadow(
                 shadow_rect,
                 shadow_radius,
                 dest_rect,
                 blur_radius,
                 clip_mode,
@@ -216,17 +220,17 @@ impl<'a> DisplayListFlattener<'a> {
                     prim_info.clone()
                 }
             };
 
             self.add_primitive(
                 clip_and_scroll,
                 &prim_info,
                 extra_clips,
-                PrimitiveContainer::Brush(prim),
+                prim,
             );
         }
     }
 }
 
 fn adjust_border_radius_for_box_shadow(radius: BorderRadius, spread_amount: f32) -> BorderRadius {
     BorderRadius {
         top_left: adjust_corner_for_box_shadow(radius.top_left, spread_amount),
--- a/gfx/wr/webrender/src/display_list_flattener.rs
+++ b/gfx/wr/webrender/src/display_list_flattener.rs
@@ -835,17 +835,19 @@ impl<'a> DisplayListFlattener<'a> {
         clip_chain_id: ClipChainId,
         spatial_node_index: SpatialNodeIndex,
         container: PrimitiveContainer,
     ) -> PrimitiveInstance {
         // Build a primitive key, and optionally an old
         // style PrimitiveDetails structure from the
         // source primitive container.
         let mut info = info.clone();
-        let (prim_key_kind, prim_details) = container.build(&mut info);
+        let (prim_key_kind, prim_details) = container.build(
+            &mut info,
+        );
 
         let prim_key = PrimitiveKey::new(
             info.is_backface_visible,
             info.rect,
             info.clip_rect,
             prim_key_kind,
         );
 
@@ -1674,26 +1676,23 @@ impl<'a> DisplayListFlattener<'a> {
     ) {
         if color.a == 0.0 {
             // Don't add transparent rectangles to the draw list, but do consider them for hit
             // testing. This allows specifying invisible hit testing areas.
             self.add_primitive_to_hit_testing_list(info, clip_and_scroll);
             return;
         }
 
-        let prim = BrushPrimitive::new(
-            BrushKind::new_solid(color),
-            None,
-        );
-
         self.add_primitive(
             clip_and_scroll,
             info,
             Vec::new(),
-            PrimitiveContainer::Brush(prim),
+            PrimitiveContainer::Rectangle {
+                color,
+            },
         );
     }
 
     pub fn add_clear_rectangle(
         &mut self,
         clip_and_scroll: ScrollNodeAndClipChain,
         info: &LayoutPrimitiveInfo,
     ) {
--- a/gfx/wr/webrender/src/frame_builder.rs
+++ b/gfx/wr/webrender/src/frame_builder.rs
@@ -80,17 +80,16 @@ pub struct FrameBuildingState<'a> {
     pub profile_counters: &'a mut FrameProfileCounters,
     pub clip_store: &'a mut ClipStore,
     pub resource_cache: &'a mut ResourceCache,
     pub gpu_cache: &'a mut GpuCache,
     pub special_render_passes: &'a mut SpecialRenderPasses,
     pub transforms: &'a mut TransformPalette,
     pub segment_builder: SegmentBuilder,
     pub surfaces: &'a mut Vec<SurfaceInfo>,
-    pub scratch: &'a mut PrimitiveScratchBuffer,
 }
 
 /// Immutable context of a picture when processing children.
 #[derive(Debug)]
 pub struct PictureContext {
     pub pic_index: PictureIndex,
     pub pipeline_id: PipelineId,
     pub apply_local_clip_rect: bool,
@@ -250,17 +249,16 @@ impl FrameBuilder {
             profile_counters,
             clip_store: &mut self.clip_store,
             resource_cache,
             gpu_cache,
             special_render_passes,
             transforms: transform_palette,
             segment_builder: SegmentBuilder::new(),
             surfaces: pic_update_state.surfaces,
-            scratch,
         };
 
         let (pic_context, mut pic_state, mut prim_list) = self
             .prim_store
             .pictures[self.root_pic_index.0]
             .take_context(
                 self.root_pic_index,
                 root_spatial_node_index,
@@ -274,16 +272,17 @@ impl FrameBuilder {
 
         self.prim_store.prepare_primitives(
             &mut prim_list,
             &pic_context,
             &mut pic_state,
             &frame_context,
             &mut frame_state,
             resources,
+            scratch,
         );
 
         let pic = &mut self.prim_store.pictures[self.root_pic_index.0];
         pic.restore_context(
             prim_list,
             pic_context,
             pic_state,
             &mut frame_state,
--- a/gfx/wr/webrender/src/picture.rs
+++ b/gfx/wr/webrender/src/picture.rs
@@ -409,24 +409,16 @@ impl TileCache {
 
                 match prim.details {
                     PrimitiveDetails::Brush(ref brush) => {
                         match brush.kind {
                             // Rectangles and images may depend on opacity bindings.
                             // TODO(gw): In future, we might be able to completely remove
                             //           opacity collapsing support. It's of limited use
                             //           once we have full picture caching.
-                            BrushKind::Solid { opacity_binding_index, .. } => {
-                                let opacity_binding = &opacity_binding_store[opacity_binding_index];
-                                for binding in &opacity_binding.bindings {
-                                    if let PropertyBinding::Binding(key, default) = binding {
-                                        opacity_bindings.push((key.id, *default));
-                                    }
-                                }
-                            }
                             BrushKind::Image { opacity_binding_index, ref request, .. } => {
                                 let opacity_binding = &opacity_binding_store[opacity_binding_index];
                                 for binding in &opacity_binding.bindings {
                                     if let PropertyBinding::Binding(key, default) = binding {
                                         opacity_bindings.push((key.id, *default));
                                     }
                                 }
 
@@ -437,16 +429,24 @@ impl TileCache {
                             }
                             BrushKind::RadialGradient { .. } |
                             BrushKind::LinearGradient { .. } => {
                             }
                         }
                     }
                 }
             }
+            PrimitiveInstanceKind::Rectangle { opacity_binding_index, .. } => {
+                let opacity_binding = &opacity_binding_store[opacity_binding_index];
+                for binding in &opacity_binding.bindings {
+                    if let PropertyBinding::Binding(key, default) = binding {
+                        opacity_bindings.push((key.id, *default));
+                    }
+                }
+            }
             PrimitiveInstanceKind::TextRun { .. } |
             PrimitiveInstanceKind::LineDecoration { .. } |
             PrimitiveInstanceKind::Clear |
             PrimitiveInstanceKind::NormalBorder { .. } |
             PrimitiveInstanceKind::ImageBorder { .. } => {
                 // These don't contribute dependencies
             }
         }
--- a/gfx/wr/webrender/src/prim_store.rs
+++ b/gfx/wr/webrender/src/prim_store.rs
@@ -7,17 +7,17 @@ use api::{DeviceIntRect, DeviceIntSize, 
 use api::{FilterOp, GlyphInstance, GradientStop, ImageKey, ImageRendering, ItemRange, TileOffset, RepeatMode};
 use api::{RasterSpace, LayoutPoint, LayoutRect, LayoutSideOffsets, LayoutSize, LayoutToWorldTransform};
 use api::{LayoutVector2D, PremultipliedColorF, PropertyBinding, Shadow, YuvColorSpace, YuvFormat, LayoutRectAu};
 use api::{DeviceIntSideOffsets, WorldPixel, BoxShadowClipMode, NormalBorder, WorldRect, LayoutToWorldScale};
 use api::{PicturePixel, RasterPixel, ColorDepth, LineStyle, LineOrientation, LayoutSizeAu, AuHelpers, LayoutVector2DAu};
 use app_units::Au;
 use border::{get_max_scale_for_border, build_border_instances, create_border_segments};
 use border::{create_nine_patch_segments, BorderSegmentCacheKey, NormalBorderAu};
-use clip::ClipStore;
+use clip::{ClipStore};
 use clip_scroll_tree::{ClipScrollTree, SpatialNodeIndex};
 use clip::{ClipDataStore, ClipNodeFlags, ClipChainId, ClipChainInstance, ClipItem, ClipNodeCollector};
 use euclid::{SideOffsets2D, TypedTransform3D, TypedRect, TypedScale};
 use frame_builder::{FrameBuildingContext, FrameBuildingState, PictureContext, PictureState};
 use frame_builder::PrimitiveContext;
 use glyph_rasterizer::{FontInstance, FontTransform, GlyphKey, FONT_SIZE_LIMIT};
 use gpu_cache::{GpuCache, GpuCacheAddress, GpuCacheHandle, GpuDataRequest, ToGpuBlocks};
 use gpu_types::BrushFlags;
@@ -28,16 +28,17 @@ use picture::{ClusterRange, PrimitiveLis
 #[cfg(debug_assertions)]
 use render_backend::{FrameId};
 use render_backend::FrameResources;
 use render_task::{BlitSource, RenderTask, RenderTaskCacheKey, RenderTaskTree, to_cache_size};
 use render_task::{RenderTaskCacheKeyKind, RenderTaskId, RenderTaskCacheEntryHandle};
 use renderer::{MAX_VERTEX_TEXTURE_WIDTH};
 use resource_cache::{ImageProperties, ImageRequest, ResourceCache};
 use scene::SceneProperties;
+use segment::SegmentBuilder;
 use std::{cmp, fmt, mem, ops, u32, usize};
 #[cfg(debug_assertions)]
 use std::sync::atomic::{AtomicUsize, Ordering};
 use storage;
 use tiling::SpecialRenderPasses;
 use util::{ScaleOffset, MatrixHelpers, MaxRect, recycle_vec};
 use util::{pack_as_float, project_rect, raster_rect_to_device_pixels};
 use smallvec::SmallVec;
@@ -95,16 +96,22 @@ impl PrimitiveOpacity {
         PrimitiveOpacity { is_opaque: false }
     }
 
     pub fn from_alpha(alpha: f32) -> PrimitiveOpacity {
         PrimitiveOpacity {
             is_opaque: alpha >= 1.0,
         }
     }
+
+    pub fn combine(&self, other: PrimitiveOpacity) -> PrimitiveOpacity {
+        PrimitiveOpacity{
+            is_opaque: self.is_opaque && other.is_opaque
+        }
+    }
 }
 
 #[derive(Debug, Copy, Clone)]
 pub enum VisibleFace {
     Front,
     Back,
 }
 
@@ -372,16 +379,19 @@ pub enum PrimitiveKeyKind {
         width: i32,
         height: i32,
         slice: SideOffsets2D<i32>,
         fill: bool,
         repeat_horizontal: RepeatMode,
         repeat_vertical: RepeatMode,
         outset: SideOffsets2D<Au>,
     },
+    Rectangle {
+        color: ColorU,
+    }
 }
 
 #[cfg_attr(feature = "capture", derive(Serialize))]
 #[cfg_attr(feature = "replay", derive(Deserialize))]
 #[derive(Debug, Clone, Eq, PartialEq, Hash)]
 pub struct PrimitiveKey {
     pub is_backface_visible: bool,
     pub prim_rect: LayoutRectAu,
@@ -434,16 +444,22 @@ impl PrimitiveKey {
                 PrimitiveInstanceKind::NormalBorder {
                     cache_handles: storage::Range::empty(),
                 }
             }
             PrimitiveKeyKind::ImageBorder { .. } => {
                 PrimitiveInstanceKind::ImageBorder {
                 }
             }
+            PrimitiveKeyKind::Rectangle { .. } => {
+                PrimitiveInstanceKind::Rectangle {
+                    opacity_binding_index: OpacityBindingIndex::INVALID,
+                    segment_instance_index: SegmentInstanceIndex::INVALID,
+                }
+            }
             PrimitiveKeyKind::Unused => {
                 // Should never be hit as this method should not be
                 // called for old style primitives.
                 unreachable!();
             }
         }
     }
 }
@@ -473,25 +489,31 @@ pub enum PrimitiveTemplateKind {
     },
     NormalBorder {
         template: Box<NormalBorderTemplate>,
     },
     ImageBorder {
         request: ImageRequest,
         brush_segments: Vec<BrushSegment>,
     },
+    Rectangle {
+        color: ColorF,
+    },
     Clear,
     Unused,
 }
 
 /// Construct the primitive template data from a primitive key. This
 /// is invoked when a primitive key is created and the interner
 /// doesn't currently contain a primitive with this key.
 impl PrimitiveKeyKind {
-    fn into_template(self, rect: &LayoutRect) -> PrimitiveTemplateKind {
+    fn into_template(
+        self,
+        rect: &LayoutRect,
+    ) -> PrimitiveTemplateKind {
         match self {
             PrimitiveKeyKind::Unused => PrimitiveTemplateKind::Unused,
             PrimitiveKeyKind::TextRun { glyphs, font, offset, .. } => {
                 PrimitiveTemplateKind::TextRun {
                     font,
                     offset,
                     glyphs,
                 }
@@ -557,16 +579,21 @@ impl PrimitiveKeyKind {
                     ),
                 );
 
                 PrimitiveTemplateKind::ImageBorder {
                     request,
                     brush_segments,
                 }
             }
+            PrimitiveKeyKind::Rectangle { color, .. } => {
+                PrimitiveTemplateKind::Rectangle {
+                    color: color.into(),
+                }
+            }
             PrimitiveKeyKind::LineDecoration { cache_key, color } => {
                 PrimitiveTemplateKind::LineDecoration {
                     cache_key,
                     color: color.into(),
                 }
             }
         }
     }
@@ -622,16 +649,23 @@ impl PrimitiveTemplate {
                     request.write_segment(
                         self.prim_rect,
                         [0.0; 4],
                     );
                 }
 
                 PrimitiveOpacity::translucent()
             }
+            PrimitiveTemplateKind::Rectangle { ref color, .. } => {
+                if let Some(mut request) = frame_state.gpu_cache.request(&mut self.gpu_cache_handle) {
+                    request.push(color.premultiplied());
+                }
+
+                PrimitiveOpacity::from_alpha(color.a)
+            }
             PrimitiveTemplateKind::NormalBorder { ref template, .. } => {
                 if let Some(mut request) = frame_state.gpu_cache.request(&mut self.gpu_cache_handle) {
                     // Border primitives currently used for
                     // image borders, and run through the
                     // normal brush_image shader.
                     request.push(PremultipliedColorF::WHITE);
                     request.push(PremultipliedColorF::WHITE);
                     request.push([
@@ -851,20 +885,16 @@ pub struct VisibleGradientTile {
 #[cfg_attr(feature = "replay", derive(Deserialize))]
 #[derive(Debug)]
 pub struct BorderSegmentInfo {
     pub local_task_size: LayoutSize,
     pub cache_key: BorderSegmentCacheKey,
 }
 
 pub enum BrushKind {
-    Solid {
-        color: ColorF,
-        opacity_binding_index: OpacityBindingIndex,
-    },
     Image {
         request: ImageRequest,
         alpha_type: AlphaType,
         stretch_size: LayoutSize,
         tile_spacing: LayoutSize,
         color: ColorF,
         source: ImageSource,
         sub_rect: Option<DeviceIntRect>,
@@ -910,31 +940,22 @@ impl BrushKind {
             BrushKind::Image { ref request, .. } => {
                 // tiled images don't support segmentation
                 resource_cache
                     .get_image_properties(request.key)
                     .and_then(|properties| properties.tiling)
                     .is_none()
             }
 
-            BrushKind::Solid { .. } |
             BrushKind::YuvImage { .. } |
             BrushKind::RadialGradient { .. } |
             BrushKind::LinearGradient { .. } => true,
         }
     }
 
-    // Construct a brush that is a solid color rectangle.
-    pub fn new_solid(color: ColorF) -> BrushKind {
-        BrushKind::Solid {
-            color,
-            opacity_binding_index: OpacityBindingIndex::INVALID,
-        }
-    }
-
     // Construct a brush that is an image wisth `stretch_size` dimensions and
     // `color`.
     pub fn new_image(
         request: ImageRequest,
         stretch_size: LayoutSize,
         color: ColorF
     ) -> BrushKind {
         BrushKind::Image {
@@ -1112,20 +1133,16 @@ impl BrushPrimitive {
                     request.push(PremultipliedColorF::WHITE);
                     request.push([
                         stretch_size.width + tile_spacing.width,
                         stretch_size.height + tile_spacing.height,
                         0.0,
                         0.0,
                     ]);
                 }
-                // Solid rects also support opacity collapsing.
-                BrushKind::Solid { ref color, .. } => {
-                    request.push(color.premultiplied());
-                }
                 BrushKind::LinearGradient { stretch_size, start_point, end_point, extend_mode, .. } => {
                     request.push([
                         start_point.x,
                         start_point.y,
                         end_point.x,
                         end_point.y,
                     ]);
                     request.push([
@@ -1720,16 +1737,19 @@ pub enum PrimitiveContainer {
         width: i32,
         height: i32,
         slice: SideOffsets2D<i32>,
         fill: bool,
         repeat_horizontal: RepeatMode,
         repeat_vertical: RepeatMode,
         outset: SideOffsets2D<f32>,
     },
+    Rectangle {
+        color: ColorF,
+    },
 }
 
 impl PrimitiveContainer {
     // Return true if the primary primitive is visible.
     // Used to trivially reject non-visible primitives.
     // TODO(gw): Currently, primitives other than those
     //           listed here are handled before the
     //           add_primitive() call. In the future
@@ -1737,32 +1757,30 @@ impl PrimitiveContainer {
     //           primitive types to use this.
     pub fn is_visible(&self) -> bool {
         match *self {
             PrimitiveContainer::TextRun { ref font, .. } => {
                 font.color.a > 0
             }
             PrimitiveContainer::Brush(ref brush) => {
                 match brush.kind {
-                    BrushKind::Solid { ref color, .. } => {
-                        color.a > 0.0
-                    }
                     BrushKind::Image { .. } |
                     BrushKind::YuvImage { .. } |
                     BrushKind::RadialGradient { .. } |
                     BrushKind::LinearGradient { .. } => {
                         true
                     }
                 }
             }
             PrimitiveContainer::NormalBorder { .. } |
             PrimitiveContainer::ImageBorder { .. } |
             PrimitiveContainer::Clear => {
                 true
             }
+            PrimitiveContainer::Rectangle { ref color, .. } |
             PrimitiveContainer::LineDecoration { ref color, .. } => {
                 color.a > 0.0
             }
         }
     }
 
     /// Convert a source primitive container into a key, and optionally
     /// an old style PrimitiveDetails structure.
@@ -1779,16 +1797,23 @@ impl PrimitiveContainer {
                     shadow,
                 };
 
                 (key, None)
             }
             PrimitiveContainer::Clear => {
                 (PrimitiveKeyKind::Clear, None)
             }
+            PrimitiveContainer::Rectangle { color, .. } => {
+                let key = PrimitiveKeyKind::Rectangle {
+                    color: color.into(),
+                };
+
+                (key, None)
+            }
             PrimitiveContainer::ImageBorder {
                 request,
                 widths,
                 width,
                 height,
                 slice,
                 fill,
                 repeat_vertical,
@@ -1915,31 +1940,30 @@ impl PrimitiveContainer {
             PrimitiveContainer::LineDecoration { style, orientation, wavy_line_thickness, .. } => {
                 PrimitiveContainer::LineDecoration {
                     color: shadow.color,
                     style,
                     orientation,
                     wavy_line_thickness,
                 }
             }
+            PrimitiveContainer::Rectangle { .. } => {
+                PrimitiveContainer::Rectangle {
+                    color: shadow.color,
+                }
+            }
             PrimitiveContainer::NormalBorder { border, widths, .. } => {
                 let border = border.with_color(shadow.color);
                 PrimitiveContainer::NormalBorder {
                     border,
                     widths,
                 }
             }
             PrimitiveContainer::Brush(ref brush) => {
                 match brush.kind {
-                    BrushKind::Solid { .. } => {
-                        PrimitiveContainer::Brush(BrushPrimitive::new(
-                            BrushKind::new_solid(shadow.color),
-                            None,
-                        ))
-                    }
                     BrushKind::Image { request, stretch_size, .. } => {
                         PrimitiveContainer::Brush(BrushPrimitive::new(
                             BrushKind::new_image(request.clone(),
                                                  stretch_size.clone(),
                                                  shadow.color),
                             None,
                         ))
                     }
@@ -2003,16 +2027,20 @@ pub enum PrimitiveInstanceKind {
         //           but also the opacity, clip_task_id etc below.
         cache_handle: Option<RenderTaskCacheEntryHandle>,
     },
     NormalBorder {
         cache_handles: storage::Range<RenderTaskCacheEntryHandle>,
     },
     ImageBorder {
     },
+    Rectangle {
+        opacity_binding_index: OpacityBindingIndex,
+        segment_instance_index: SegmentInstanceIndex,
+    },
     /// Clear out a rect, used for special effects.
     Clear,
 }
 
 #[derive(Clone, Debug)]
 pub struct PrimitiveInstance {
     /// Identifies the kind of primitive this
     /// instance is, and references to where
@@ -2086,22 +2114,32 @@ impl PrimitiveInstance {
     }
 
     #[cfg(not(debug_assertions))]
     pub fn is_chased(&self) -> bool {
         false
     }
 }
 
+#[derive(Debug)]
+pub struct SegmentedInstance {
+    pub gpu_cache_handle: GpuCacheHandle,
+    pub segments_range: SegmentsRange,
+}
+
 pub type GlyphKeyStorage = storage::Storage<GlyphKey>;
 pub type TextRunIndex = storage::Index<TextRunPrimitive>;
 pub type TextRunStorage = storage::Storage<TextRunPrimitive>;
 pub type OpacityBindingIndex = storage::Index<OpacityBinding>;
 pub type OpacityBindingStorage = storage::Storage<OpacityBinding>;
 pub type BorderHandleStorage = storage::Storage<RenderTaskCacheEntryHandle>;
+pub type SegmentStorage = storage::Storage<BrushSegment>;
+pub type SegmentsRange = storage::Range<BrushSegment>;
+pub type SegmentInstanceStorage = storage::Storage<SegmentedInstance>;
+pub type SegmentInstanceIndex = storage::Index<SegmentedInstance>;
 
 /// Contains various vecs of data that is used only during frame building,
 /// where we want to recycle the memory each new display list, to avoid constantly
 /// re-allocating and moving memory around. Written during primitive preparation,
 /// and read during batching.
 pub struct PrimitiveScratchBuffer {
     /// Contains a list of clip mask instance parameters
     /// per segment generated.
@@ -2109,31 +2147,43 @@ pub struct PrimitiveScratchBuffer {
 
     /// List of glyphs keys that are allocated by each
     /// text run instance.
     pub glyph_keys: GlyphKeyStorage,
 
     /// List of render task handles for border segment instances
     /// that have been added this frame.
     pub border_cache_handles: BorderHandleStorage,
+
+    /// A list of brush segments that have been built for this scene.
+    pub segments: SegmentStorage,
+
+    /// A list of segment ranges and GPU cache handles for prim instances
+    /// that have opted into segment building. In future, this should be
+    /// removed in favor of segment building during primitive interning.
+    pub segment_instances: SegmentInstanceStorage,
 }
 
 impl PrimitiveScratchBuffer {
     pub fn new() -> Self {
         PrimitiveScratchBuffer {
             clip_mask_instances: Vec::new(),
             glyph_keys: GlyphKeyStorage::new(0),
             border_cache_handles: BorderHandleStorage::new(0),
+            segments: SegmentStorage::new(0),
+            segment_instances: SegmentInstanceStorage::new(0),
         }
     }
 
     pub fn recycle(&mut self) {
         recycle_vec(&mut self.clip_mask_instances);
         self.glyph_keys.recycle();
         self.border_cache_handles.recycle();
+        self.segments.recycle();
+        self.segment_instances.recycle();
     }
 
     pub fn begin_frame(&mut self) {
         // Clear the clip mask tasks for the beginning of the frame. Append
         // a single kind representing no clip mask, at the ClipTaskIndex::INVALID
         // location.
         self.clip_mask_instances.clear();
         self.clip_mask_instances.push(ClipMaskKind::None);
@@ -2277,32 +2327,35 @@ impl PrimitiveStore {
         }
     }
 
     // Internal method that retrieves the primitive index of a primitive
     // that can be the target for collapsing parent opacity filters into.
     fn get_opacity_collapse_prim(
         &self,
         pic_index: PictureIndex,
-    ) -> Option<PrimitiveIndex> {
+    ) -> Option<PictureIndex> {
         let pic = &self.pictures[pic_index.0];
 
         // We can only collapse opacity if there is a single primitive, otherwise
         // the opacity needs to be applied to the primitives as a group.
         if pic.prim_list.prim_instances.len() != 1 {
             return None;
         }
 
         let prim_instance = &pic.prim_list.prim_instances[0];
 
         // For now, we only support opacity collapse on solid rects and images.
         // This covers the most common types of opacity filters that can be
         // handled by this optimization. In the future, we can easily extend
         // this to other primitives, such as text runs and gradients.
         match prim_instance.kind {
+            PrimitiveInstanceKind::Rectangle { .. } => {
+                return Some(pic_index);
+            }
             PrimitiveInstanceKind::Clear |
             PrimitiveInstanceKind::TextRun { .. } |
             PrimitiveInstanceKind::NormalBorder { .. } |
             PrimitiveInstanceKind::ImageBorder { .. } |
             PrimitiveInstanceKind::LineDecoration { .. } => {
                 // TODO: Once rectangles and/or images are ported
                 //       to use interned primitives, we will need
                 //       to handle opacity collapse here.
@@ -2319,19 +2372,18 @@ impl PrimitiveStore {
             }
             PrimitiveInstanceKind::LegacyPrimitive { prim_index } => {
                 let prim = &self.primitives[prim_index.0];
                 match prim.details {
                     PrimitiveDetails::Brush(ref brush) => {
                         match brush.kind {
                             // If we find a single rect or image, we can use that
                             // as the primitive to collapse the opacity into.
-                            BrushKind::Solid { .. } |
                             BrushKind::Image { .. } => {
-                                return Some(prim_index)
+                                return Some(pic_index)
                             }
                             BrushKind::YuvImage { .. } |
                             BrushKind::LinearGradient { .. } |
                             BrushKind::RadialGradient { .. } => {}
                         }
                     }
                 }
             }
@@ -2356,38 +2408,53 @@ impl PrimitiveStore {
             _ => {
                 return;
             }
         };
 
         // See if this picture contains a single primitive that supports
         // opacity collapse.
         match self.get_opacity_collapse_prim(pic_index) {
-            Some(prim_index) => {
-                let prim = &mut self.primitives[prim_index.0];
-                match prim.details {
-                    PrimitiveDetails::Brush(ref mut brush) => {
-                        // By this point, we know we should only have found a primitive
-                        // that supports opacity collapse.
-                        match brush.kind {
-                            BrushKind::Solid { ref mut opacity_binding_index, .. } |
-                            BrushKind::Image { ref mut opacity_binding_index, .. } => {
-                                if *opacity_binding_index == OpacityBindingIndex::INVALID {
-                                    *opacity_binding_index = self.opacity_bindings.push(OpacityBinding::new());
+            Some(pic_index) => {
+                let pic = &mut self.pictures[pic_index.0];
+                let prim_instance = &mut pic.prim_list.prim_instances[0];
+                match prim_instance.kind {
+                    PrimitiveInstanceKind::Rectangle { ref mut opacity_binding_index, .. } => {
+                        if *opacity_binding_index == OpacityBindingIndex::INVALID {
+                            *opacity_binding_index = self.opacity_bindings.push(OpacityBinding::new());
+                        }
+                        let opacity_binding = &mut self.opacity_bindings[*opacity_binding_index];
+                        opacity_binding.push(binding);
+                    }
+                    PrimitiveInstanceKind::LegacyPrimitive { prim_index } => {
+                        let prim = &mut self.primitives[prim_index.0];
+                        match prim.details {
+                            PrimitiveDetails::Brush(ref mut brush) => {
+                                // By this point, we know we should only have found a primitive
+                                // that supports opacity collapse.
+                                match brush.kind {
+                                    BrushKind::Image { ref mut opacity_binding_index, .. } => {
+                                        if *opacity_binding_index == OpacityBindingIndex::INVALID {
+                                            *opacity_binding_index = self.opacity_bindings.push(OpacityBinding::new());
+                                        }
+                                        let opacity_binding = &mut self.opacity_bindings[*opacity_binding_index];
+                                        opacity_binding.push(binding);
+                                    }
+                                    BrushKind::YuvImage { .. } |
+                                    BrushKind::LinearGradient { .. } |
+                                    BrushKind::RadialGradient { .. } => {
+                                        unreachable!("bug: invalid prim type for opacity collapse");
+                                    }
                                 }
-                                let opacity_binding = &mut self.opacity_bindings[*opacity_binding_index];
-                                opacity_binding.push(binding);
-                            }
-                            BrushKind::YuvImage { .. } |
-                            BrushKind::LinearGradient { .. } |
-                            BrushKind::RadialGradient { .. } => {
-                                unreachable!("bug: invalid prim type for opacity collapse");
                             }
                         }
                     }
+                    _ => {
+                        unreachable!();
+                    }
                 }
             }
             None => {
                 return;
             }
         }
 
         // The opacity filter has been collapsed, so mark this picture
@@ -2408,16 +2475,17 @@ impl PrimitiveStore {
         prim_context: &PrimitiveContext,
         pic_context: &PictureContext,
         pic_state: &mut PictureState,
         frame_context: &FrameBuildingContext,
         frame_state: &mut FrameBuildingState,
         display_list: &BuiltDisplayList,
         plane_split_anchor: usize,
         resources: &mut FrameResources,
+        scratch: &mut PrimitiveScratchBuffer,
     ) -> bool {
         // If we have dependencies, we need to prepare them first, in order
         // to know the actual rect of this primitive.
         // For example, scrolling may affect the location of an item in
         // local space, which may force us to render this item on a larger
         // picture target, if being composited.
         let pic_info = {
             match prim_instance.kind {
@@ -2439,16 +2507,17 @@ impl PrimitiveStore {
                                 println!("\tculled for carrying an invisible composite filter");
                             }
 
                             return false;
                         }
                     }
                 }
                 PrimitiveInstanceKind::TextRun { .. } |
+                PrimitiveInstanceKind::Rectangle { .. } |
                 PrimitiveInstanceKind::LineDecoration { .. } |
                 PrimitiveInstanceKind::LegacyPrimitive { .. } |
                 PrimitiveInstanceKind::NormalBorder { .. } |
                 PrimitiveInstanceKind::ImageBorder { .. } |
                 PrimitiveInstanceKind::Clear => {
                     None
                 }
             }
@@ -2461,16 +2530,17 @@ impl PrimitiveStore {
 
                 self.prepare_primitives(
                     &mut prim_list,
                     &pic_context_for_children,
                     &mut pic_state_for_children,
                     frame_context,
                     frame_state,
                     resources,
+                    scratch,
                 );
 
                 if !pic_state_for_children.is_cacheable {
                     pic_state.is_cacheable = false;
                 }
 
                 // Restore the dependencies (borrow check dance)
                 let clip_node_collector = self
@@ -2493,16 +2563,17 @@ impl PrimitiveStore {
             PrimitiveInstanceKind::Picture { pic_index } => {
                 let pic = &self.pictures[pic_index.0];
                 (pic.local_rect, LayoutRect::max_rect())
             }
             PrimitiveInstanceKind::TextRun { .. } |
             PrimitiveInstanceKind::Clear |
             PrimitiveInstanceKind::NormalBorder { .. } |
             PrimitiveInstanceKind::ImageBorder { .. } |
+            PrimitiveInstanceKind::Rectangle { .. } |
             PrimitiveInstanceKind::LineDecoration { .. } => {
                 let prim_data = &resources
                     .prim_data_store[prim_instance.prim_data_handle];
                 (prim_data.prim_rect, prim_data.clip_rect)
             }
             PrimitiveInstanceKind::LegacyPrimitive { prim_index } => {
                 let prim = &self.primitives[prim_index.0];
                 (prim.local_rect, prim.local_clip_rect)
@@ -2628,16 +2699,17 @@ impl PrimitiveStore {
                 &clip_chain,
                 pic_context,
                 pic_state,
                 frame_context,
                 frame_state,
                 &clip_node_collector,
                 &mut self.primitives,
                 resources,
+                scratch,
             );
 
             if prim_instance.is_chased() {
                 println!("\tconsidered visible and ready with local rect {:?}", local_rect);
             }
         }
 
         #[cfg(debug_assertions)]
@@ -2683,26 +2755,28 @@ impl PrimitiveStore {
                     request.write_segment(
                         pic.local_rect,
                         [0.0; 4],
                     );
                 }
             }
             PrimitiveInstanceKind::TextRun { .. } |
             PrimitiveInstanceKind::Clear |
+            PrimitiveInstanceKind::Rectangle { .. } |
             PrimitiveInstanceKind::NormalBorder { .. } |
             PrimitiveInstanceKind::ImageBorder { .. } |
             PrimitiveInstanceKind::LineDecoration { .. } => {
                 self.prepare_interned_prim_for_render(
                     prim_instance,
                     prim_context,
                     pic_context,
                     frame_context,
                     frame_state,
                     resources,
+                    scratch,
                 );
             }
             PrimitiveInstanceKind::LegacyPrimitive { prim_index } => {
                 let prim_details = &mut self.primitives[prim_index.0].details;
 
                 prim_instance.prepare_prim_for_render_inner(
                     prim_local_rect,
                     prim_details,
@@ -2723,16 +2797,17 @@ impl PrimitiveStore {
     pub fn prepare_primitives(
         &mut self,
         prim_list: &mut PrimitiveList,
         pic_context: &PictureContext,
         pic_state: &mut PictureState,
         frame_context: &FrameBuildingContext,
         frame_state: &mut FrameBuildingState,
         resources: &mut FrameResources,
+        scratch: &mut PrimitiveScratchBuffer,
     ) {
         let display_list = &frame_context
             .pipelines
             .get(&pic_context.pipeline_id)
             .expect("No display list?")
             .display_list;
 
         for (plane_split_anchor, prim_instance) in prim_list.prim_instances.iter_mut().enumerate() {
@@ -2803,16 +2878,17 @@ impl PrimitiveStore {
                 &prim_context,
                 pic_context,
                 pic_state,
                 frame_context,
                 frame_state,
                 display_list,
                 plane_split_anchor,
                 resources,
+                scratch,
             ) {
                 frame_state.profile_counters.visible_primitives.inc();
             }
         }
     }
 
     /// Prepare an interned primitive for rendering, by requesting
     /// resources, render tasks etc. This is equivalent to the
@@ -2820,16 +2896,17 @@ impl PrimitiveStore {
     fn prepare_interned_prim_for_render(
         &mut self,
         prim_instance: &mut PrimitiveInstance,
         prim_context: &PrimitiveContext,
         pic_context: &PictureContext,
         frame_context: &FrameBuildingContext,
         frame_state: &mut FrameBuildingState,
         resources: &mut FrameResources,
+        scratch: &mut PrimitiveScratchBuffer,
     ) {
         let prim_data = &mut resources
             .prim_data_store[prim_instance.prim_data_handle];
 
         // Update the template this instane references, which may refresh the GPU
         // cache with any shared template data.
         prim_data.update(
             frame_state,
@@ -2902,17 +2979,17 @@ impl PrimitiveStore {
                     glyphs,
                     frame_context.device_pixel_scale,
                     &transform,
                     pic_context,
                     frame_state.resource_cache,
                     frame_state.gpu_cache,
                     frame_state.render_tasks,
                     frame_state.special_render_passes,
-                    frame_state.scratch,
+                    scratch,
                 );
             }
             (
                 PrimitiveInstanceKind::Clear,
                 PrimitiveTemplateKind::Clear
             ) => {
                 // Nothing specific to prepare for clear rects, since the
                 // GPU cache is updated by the template earlier.
@@ -2964,26 +3041,52 @@ impl PrimitiveStore {
 
                             surfaces[pic_context.surface_index.0].tasks.push(task_id);
 
                             task_id
                         }
                     ));
                 }
 
-                *cache_handles = frame_state
-                    .scratch
+                *cache_handles = scratch
                     .border_cache_handles
                     .extend(handles);
             }
             (
                 PrimitiveInstanceKind::ImageBorder { .. },
                 PrimitiveTemplateKind::ImageBorder { .. }
             ) => {
             }
+            (
+                PrimitiveInstanceKind::Rectangle { segment_instance_index, opacity_binding_index, .. },
+                PrimitiveTemplateKind::Rectangle { ref color, .. }
+            ) => {
+                if *segment_instance_index != SegmentInstanceIndex::UNUSED {
+                    let segment_instance = &mut scratch.segment_instances[*segment_instance_index];
+
+                    if let Some(mut request) = frame_state.gpu_cache.request(&mut segment_instance.gpu_cache_handle) {
+                        let segments = &scratch.segments[segment_instance.segments_range];
+
+                        request.push(color.premultiplied());
+
+                        for segment in segments {
+                            request.write_segment(
+                                segment.local_rect,
+                                [0.0; 4],
+                            );
+                        }
+                    }
+                }
+
+                update_opacity_binding(
+                    &mut self.opacity_bindings,
+                    *opacity_binding_index,
+                    frame_context.scene_properties,
+                );
+            }
             _ => {
                 unreachable!();
             }
         }
     }
 }
 
 fn build_gradient_stops_request(
@@ -3098,63 +3201,46 @@ impl<'a> GpuDataRequest<'a> {
         extra_data: [f32; 4],
     ) {
         let _ = VECS_PER_SEGMENT;
         self.push(local_rect);
         self.push(extra_data);
     }
 }
 
-impl BrushPrimitive {
     fn write_brush_segment_description(
-        &mut self,
         prim_local_rect: LayoutRect,
         prim_local_clip_rect: LayoutRect,
         clip_chain: &ClipChainInstance,
-        frame_state: &mut FrameBuildingState,
+        segment_builder: &mut SegmentBuilder,
+        clip_store: &ClipStore,
         resources: &FrameResources,
-    ) {
-        match self.segment_desc {
-            Some(..) => {
-                // If we already have a segment descriptor, skip segment build.
-                return;
-            }
-            None => {
-                // If no segment descriptor built yet, see if it is a brush
-                // type that wants to be segmented.
-                if !self.kind.supports_segments(frame_state.resource_cache) {
-                    return;
-                }
-            }
-        }
-
+    ) -> bool {
         // If the brush is small, we generally want to skip building segments
         // and just draw it as a single primitive with clip mask. However,
         // if the clips are purely rectangles that have no per-fragment
         // clip masks, we will segment anyway. This allows us to completely
         // skip allocating a clip mask in these cases.
         let is_large = prim_local_rect.size.area() > MIN_BRUSH_SPLIT_AREA;
 
         // TODO(gw): We should probably detect and store this on each
         //           ClipSources instance, to avoid having to iterate
         //           the clip sources here.
         let mut rect_clips_only = true;
 
-        let segment_builder = &mut frame_state.segment_builder;
         segment_builder.initialize(
             prim_local_rect,
             None,
             prim_local_clip_rect
         );
 
         // Segment the primitive on all the local-space clip sources that we can.
         let mut local_clip_count = 0;
         for i in 0 .. clip_chain.clips_range.count {
-            let clip_instance = frame_state
-                .clip_store
+            let clip_instance = clip_store
                 .get_instance_from_range(&clip_chain.clips_range, i);
             let clip_node = &resources.clip_data_store[clip_instance.handle];
 
             // If this clip item is positioned by another positioning node, its relative position
             // could change during scrolling. This means that we would need to resegment. Instead
             // of doing that, only segment with clips that have the same positioning node.
             // TODO(mrobinson, #2858): It may make sense to include these nodes, resegmenting only
             // when necessary while scrolling.
@@ -3236,69 +3322,125 @@ impl BrushPrimitive {
                             ),
                         );
 
                         segment_builder.push_mask_region(rect, LayoutRect::zero(), None);
                     }
                 }
             }
 
-            match self.segment_desc {
-                Some(..) => panic!("bug: should not already have descriptor"),
-                None => {
-                    // TODO(gw): We can probably make the allocation
-                    //           patterns of this and the segment
-                    //           builder significantly better, by
-                    //           retaining it across primitives.
-                    let mut segments = BrushSegmentVec::new();
-
-                    segment_builder.build(|segment| {
-                        segments.push(
-                            BrushSegment::new(
-                                segment.rect,
-                                segment.has_mask,
-                                segment.edge_flags,
-                                [0.0; 4],
-                                BrushFlags::empty(),
-                            ),
-                        );
-                    });
-
-                    self.segment_desc = Some(BrushSegmentDescriptor {
-                        segments,
-                    });
-                }
-            }
+            return true
         }
+
+        false
     }
-}
 
 impl PrimitiveInstance {
     fn build_segments_if_needed(
         &mut self,
         prim_local_rect: LayoutRect,
         prim_local_clip_rect: LayoutRect,
         prim_clip_chain: &ClipChainInstance,
         frame_state: &mut FrameBuildingState,
         primitives: &mut [Primitive],
         resources: &FrameResources,
+        scratch: &mut PrimitiveScratchBuffer,
     ) {
-        if let PrimitiveInstanceKind::LegacyPrimitive { prim_index } = self.kind {
-            let prim = &mut primitives[prim_index.0];
-            match prim.details {
-                PrimitiveDetails::Brush(ref mut brush) => {
-                    brush.write_brush_segment_description(
+        match self.kind {
+            PrimitiveInstanceKind::Rectangle { ref mut segment_instance_index, .. } => {
+                if *segment_instance_index == SegmentInstanceIndex::INVALID {
+                    let mut segments: SmallVec<[BrushSegment; 8]> = SmallVec::new();
+
+                    if write_brush_segment_description(
                         prim_local_rect,
                         prim_local_clip_rect,
                         prim_clip_chain,
-                        frame_state,
+                        &mut frame_state.segment_builder,
+                        frame_state.clip_store,
                         resources,
-                    );
+                    ) {
+                        frame_state.segment_builder.build(|segment| {
+                            segments.push(
+                                BrushSegment::new(
+                                    segment.rect,
+                                    segment.has_mask,
+                                    segment.edge_flags,
+                                    [0.0; 4],
+                                    BrushFlags::empty(),
+                                ),
+                            );
+                        });
+                    }
+
+                    if segments.is_empty() {
+                        *segment_instance_index = SegmentInstanceIndex::UNUSED;
+                    } else {
+                        let segments_range = scratch
+                            .segments
+                            .extend(segments);
+
+                        let instance = SegmentedInstance {
+                            segments_range,
+                            gpu_cache_handle: GpuCacheHandle::new(),
+                        };
+
+                        *segment_instance_index = scratch
+                            .segment_instances
+                            .push(instance);
+                    };
                 }
             }
+            PrimitiveInstanceKind::LegacyPrimitive { prim_index } => {
+                let prim = &mut primitives[prim_index.0];
+                match prim.details {
+                    PrimitiveDetails::Brush(ref mut brush) => {
+                        match brush.segment_desc {
+                            Some(..) => {
+                                // If we already have a segment descriptor, skip segment build.
+                                return;
+                            }
+                            None => {
+                                // If no segment descriptor built yet, see if it is a brush
+                                // type that wants to be segmented.
+                                if brush.kind.supports_segments(frame_state.resource_cache) {
+                                    let mut segments = BrushSegmentVec::new();
+
+                                    if write_brush_segment_description(
+                                        prim_local_rect,
+                                        prim_local_clip_rect,
+                                        prim_clip_chain,
+                                        &mut frame_state.segment_builder,
+                                        frame_state.clip_store,
+                                        resources,
+                                    ) {
+                                        frame_state.segment_builder.build(|segment| {
+                                            segments.push(
+                                                BrushSegment::new(
+                                                    segment.rect,
+                                                    segment.has_mask,
+                                                    segment.edge_flags,
+                                                    [0.0; 4],
+                                                    BrushFlags::empty(),
+                                                ),
+                                            );
+                                        });
+                                    }
+
+                                    if !segments.is_empty() {
+                                        brush.segment_desc = Some(BrushSegmentDescriptor {
+                                            segments,
+                                        });
+                                    }
+                                }
+                            }
+                        }
+                    }
+                }
+            }
+            _ => {}
         }
     }
 
     fn update_clip_task_for_brush(
         &mut self,
         prim_local_clip_rect: LayoutRect,
         root_spatial_node_index: SpatialNodeIndex,
         prim_bounding_rect: WorldRect,
@@ -3306,32 +3448,44 @@ impl PrimitiveInstance {
         prim_clip_chain: &ClipChainInstance,
         pic_context: &PictureContext,
         pic_state: &mut PictureState,
         frame_context: &FrameBuildingContext,
         frame_state: &mut FrameBuildingState,
         clip_node_collector: &Option<ClipNodeCollector>,
         primitives: &[Primitive],
         resources: &mut FrameResources,
+        scratch: &mut PrimitiveScratchBuffer,
     ) -> bool {
         let segments = match self.kind {
             PrimitiveInstanceKind::Picture { .. } |
             PrimitiveInstanceKind::TextRun { .. } |
             PrimitiveInstanceKind::Clear |
             PrimitiveInstanceKind::LineDecoration { .. } => {
                 return false;
             }
+            PrimitiveInstanceKind::Rectangle { segment_instance_index, .. } => {
+                debug_assert!(segment_instance_index != SegmentInstanceIndex::INVALID);
+
+                if segment_instance_index == SegmentInstanceIndex::UNUSED {
+                    return false;
+                }
+
+                let segment_instance = &scratch.segment_instances[segment_instance_index];
+
+                &mut scratch.segments[segment_instance.segments_range]
+            }
             PrimitiveInstanceKind::ImageBorder { .. } => {
                 let prim_data = &resources.prim_data_store[self.prim_data_handle];
 
                 // TODO: This is quite messy - once we remove legacy primitives we
                 //       can change this to be a tuple match on (instance, template)
                 match prim_data.kind {
                     PrimitiveTemplateKind::ImageBorder { ref brush_segments, .. } => {
-                        brush_segments
+                        brush_segments.as_slice()
                     }
                     _ => {
                         unreachable!();
                     }
                 }
             }
             PrimitiveInstanceKind::NormalBorder { .. } => {
                 let prim_data = &resources.prim_data_store[self.prim_data_handle];
@@ -3368,34 +3522,34 @@ impl PrimitiveInstance {
         // clip task instance location below.
         if segments.is_empty() {
             return true;
         }
 
         // Set where in the clip mask instances array the clip mask info
         // can be found for this primitive. Each segment will push the
         // clip mask information for itself in update_clip_task below.
-        self.clip_task_index = ClipTaskIndex(frame_state.scratch.clip_mask_instances.len() as _);
+        self.clip_task_index = ClipTaskIndex(scratch.clip_mask_instances.len() as _);
 
         // If we only built 1 segment, there is no point in re-running
         // the clip chain builder. Instead, just use the clip chain
         // instance that was built for the main primitive. This is a
         // significant optimization for the common case.
         if segments.len() == 1 {
             let clip_mask_kind = segments[0].update_clip_task(
                 Some(prim_clip_chain),
                 prim_bounding_rect,
                 root_spatial_node_index,
                 pic_context.surface_index,
                 pic_state,
                 frame_context,
                 frame_state,
                 &mut resources.clip_data_store,
             );
-            frame_state.scratch.clip_mask_instances.push(clip_mask_kind);
+            scratch.clip_mask_instances.push(clip_mask_kind);
         } else {
             for segment in segments {
                 // Build a clip chain for the smaller segment rect. This will
                 // often manage to eliminate most/all clips, and sometimes
                 // clip the segment completely.
                 let segment_clip_chain = frame_state
                     .clip_store
                     .build_clip_chain_instance(
@@ -3419,17 +3573,17 @@ impl PrimitiveInstance {
                     prim_bounding_rect,
                     root_spatial_node_index,
                     pic_context.surface_index,
                     pic_state,
                     frame_context,
                     frame_state,
                     &mut resources.clip_data_store,
                 );
-                frame_state.scratch.clip_mask_instances.push(clip_mask_kind);
+                scratch.clip_mask_instances.push(clip_mask_kind);
             }
         }
 
         true
     }
 
     fn prepare_prim_for_render_inner(
         &mut self,
@@ -3797,24 +3951,16 @@ impl PrimitiveInstance {
                         let stride = stretch_size + tile_spacing;
                         if stride.width >= prim_local_rect.size.width &&
                            stride.height >= prim_local_rect.size.height {
                             stops_opacity
                         } else {
                             PrimitiveOpacity::translucent()
                         }
                     }
-                    BrushKind::Solid { ref color, opacity_binding_index, .. } => {
-                        let current_opacity = update_opacity_binding(
-                            opacity_bindings,
-                            opacity_binding_index,
-                            frame_context.scene_properties,
-                        );
-                        PrimitiveOpacity::from_alpha(current_opacity * color.a)
-                    }
                 };
             }
         }
 
         if is_tiled {
             // we already requested each tile's gpu data.
             return;
         }
@@ -3840,47 +3986,50 @@ impl PrimitiveInstance {
         clip_chain: &ClipChainInstance,
         pic_context: &PictureContext,
         pic_state: &mut PictureState,
         frame_context: &FrameBuildingContext,
         frame_state: &mut FrameBuildingState,
         clip_node_collector: &Option<ClipNodeCollector>,
         primitives: &mut [Primitive],
         resources: &mut FrameResources,
+        scratch: &mut PrimitiveScratchBuffer,
     ) {
         if self.is_chased() {
             println!("\tupdating clip task with pic rect {:?}", clip_chain.pic_clip_rect);
         }
 
         // Reset clips from previous frames since we may clip differently each frame.
         self.clip_task_index = ClipTaskIndex::INVALID;
 
         self.build_segments_if_needed(
             prim_local_rect,
             prim_local_clip_rect,
             clip_chain,
             frame_state,
             primitives,
             resources,
+            scratch,
         );
 
         // First try to  render this primitive's mask using optimized brush rendering.
         if self.update_clip_task_for_brush(
             prim_local_clip_rect,
             root_spatial_node_index,
             prim_bounding_rect,
             prim_context,
             &clip_chain,
             pic_context,
             pic_state,
             frame_context,
             frame_state,
             clip_node_collector,
             primitives,
             resources,
+            scratch,
         ) {
             if self.is_chased() {
                 println!("\tsegment tasks have been created for clipping");
             }
             return;
         }
 
         if clip_chain.needs_mask {
@@ -3903,18 +4052,18 @@ impl PrimitiveInstance {
                 );
 
                 let clip_task_id = frame_state.render_tasks.add(clip_task);
                 if self.is_chased() {
                     println!("\tcreated task {:?} with device rect {:?}",
                         clip_task_id, device_rect);
                 }
                 // Set the global clip mask instance for this primitive.
-                let clip_task_index = ClipTaskIndex(frame_state.scratch.clip_mask_instances.len() as _);
-                frame_state.scratch.clip_mask_instances.push(ClipMaskKind::Mask(clip_task_id));
+                let clip_task_index = ClipTaskIndex(scratch.clip_mask_instances.len() as _);
+                scratch.clip_mask_instances.push(ClipMaskKind::Mask(clip_task_id));
                 self.clip_task_index = clip_task_index;
                 frame_state.surfaces[pic_context.surface_index.0].tasks.push(clip_task_id);
             }
         }
     }
 }
 
 pub fn get_raster_rects(
--- a/gfx/wr/webrender/src/storage.rs
+++ b/gfx/wr/webrender/src/storage.rs
@@ -26,16 +26,17 @@ impl<T> PartialEq for Index<T> {
 
 impl<T> Index<T> {
     fn new(idx: usize) -> Self {
         debug_assert!(idx < u32::max_value() as usize);
         Index(idx as u32, PhantomData)
     }
 
     pub const INVALID: Index<T> = Index(u32::MAX, PhantomData);
+    pub const UNUSED: Index<T> = Index(u32::MAX-1, PhantomData);
 }
 
 #[derive(Debug)]
 pub struct Range<T> {
     pub start: Index<T>,
     pub end: Index<T>,
 }
 
--- a/gfx/wr/webrender/src/surface.rs
+++ b/gfx/wr/webrender/src/surface.rs
@@ -238,16 +238,17 @@ impl SurfaceDescriptor {
             match prim_instance.kind {
                 PrimitiveInstanceKind::Picture { .. } |
                 PrimitiveInstanceKind::LegacyPrimitive { .. } => {
                     return None;
                 }
                 PrimitiveInstanceKind::LineDecoration { .. } |
                 PrimitiveInstanceKind::TextRun { .. } |
                 PrimitiveInstanceKind::NormalBorder { .. } |
+                PrimitiveInstanceKind::Rectangle { .. } |
                 PrimitiveInstanceKind::ImageBorder { .. } |
                 PrimitiveInstanceKind::Clear => {}
             }
 
             // Record the unique identifier for the content represented
             // by this primitive.
             primitive_ids.push(prim_instance.prim_data_handle.uid());
         }
index f413da144e920ef21536abb4dc2f484665072af3..6b5299f386e4e6941e920d45a8629978a5fed6f6
GIT binary patch
literal 1630
zc$`g@ZB&wH7{^(@z#Pg`jYJc9R$8Xk3}#w_@f3=f$0G{3(tI1fQLE5W(}L-f9yKRw
zT3P0_tZbT?XGM<8P9rCdOw*|)>hgum3|2@~2HTVSvJdxtpa1pcx~~8KoZnp(7Rt1>
zakN39P`0cfMmX~FKOKxUavz$k;-FC0t}F(`$*OsEA*wWbD|Y9=uAs+@6M_fs3R_uJ
zTTqXYO~3G&`)AsE)G4ukv;Ot_cgLtAn&^gGb-BQt;{S9zzVc3@zuDg${*Y7L%$GL&
zIVkcT@$@d-(|At4dVf1{9U6D>^v@ctO@tP64z^|obIDT6J%3{=SZH=Jm+UtGeS;-_
zCgb#OyZipN21>@nOaA57RwGa3i;1#+`G}+E@?5`aeczplj_-+&mIy>XY_?m-c)A#S
z|4YSfO4YnWR${%^lP9Jvn1~fqy_~jW@|g$s_)OzI;EGEZk)0k%S=DuK`qxFL6B~+!
z%t7_u+58(`Thp&WVFwvXFO_lh)Ki_%&DdXZlq7Z*-|cjZ6Zz&<ydF7nl_oO{v9)y$
zDZnjY=%;HMc3;Js5$fbsxOJvg#C-T_*@i#hhl%DPh#<SfFLm)y!Aib14Y=aj^~$Z9
z(jBaiBYXHDZBGhqhdPV7Lgx}v`wSE&{p~~KO1ypUhw|rkd8Z4qt*6Gz8|k<~!yOXr
z^`L&!+!#@cNJ>(qGhb$OS6Kx%j?19FHyJtZ{igTja!BVfJ`DCxQ2~2lSjLzmDaNWj
z-}i#{F$_9Sg7d#p21v11?XYj4u@<hwO}q-==-oOj@!vek8bw1jg~7LWG}W5~;BLic
z6^wo|!{D^}rEq_VwRJhUjnql%99Y-a_Q@J2R#Ss1t<FI)x&<?-LKdPIpNGh0r)={o
z3w$ZTnvk+LNo66LXfozyYyfW@JDJ>@1~m989O6w)N}C$!W0l_7C7xT0o|M)^pySnk
zH{1_d3nN2Q%jOrcFDLT3xz5#{A4E^Vk{l+_wU@7*%kjm8gOi5|#?t2C8c5xJ6t`>z
zem3`Z`C44XaZ-GA!R&#B5qH&?>c_jC)8K5!Y`?pz<C9Q>^Vese*{H4~LJotHz#6-$
zmM|Sdk{quETb?`lW9AY$x-3Z~T?f`a4}$p<OhRUWq12dVr3uw!PsApa5q+>y)GAFN
zqHsh@136>twM`gZ$FDV-XoaZk_4+$H9c0<(SN;6B7`Nt!aV1r^SPv6QR^ON^c!s}s
zU)>$8Tik_BSZWie<+m93Ix6T+76$UqnRkBosLKQ%rQ4AdaccY=QHP95R2ZBxfweiu
z-X1?8q>Mz3U*evPs$P9u+zw$EVir>Hnix&sL~L4F=wYAi{dmm*M9n~0#S1SjvB+kD
z{y%qC+9mK^laQ5+r3|ZV#K*-{n9wY?=OMy8my-%u2)C8wtmj$vg4&*~M*!F$P5Lhu
zBlvmK8!8~4@kDPfTedv;gh86XtwAG6y21n`;kHRCl5hh|2s&Cwxy0`UJsw&p>Afuw
zc5pSD%*?CY0w)1F6Upu!E}Ums5kSlWEM5caFIHUd6Qp!H2CWie^|x>dbukQY$>Z7y
z(UCst6b+p!K8UCO69iEe(8!VGD>S)j2-1S3{j_Xk(&gyH;p^sEA}9l{4va|=GoHz5
zRkI?onq6na>qf!Ah3@LH@g}d+vK@wLWRQRkIpvIfZg$tanV_J5!JxrHU3awFX3#**
z1S~cJhHcGa@0e7Nu<dVpki7P_nz@Y20bZi%0+<jYEE#2!lYy$6dCF$>3}_wUIF(VU
zDsS#7jw4sgKUw7`IAFh<y?p;B{Q!i$<++4`wlp$=ft`p&tl+NihKUV;<&53AqyI=;
zS(S?^T~q-8FM%e7do$K~U9vX4?5&YEtjA%Na5NwgbW%fF<grNq_!7SeWi9J#R<5#C
zD}%5IIxbw}KZ^4#%2)PkB})ZEqb8GLued)=8zw5^P9~VD1$T=h?JUkqJ{|(#jYUh$
zWF$5s%rvI~J2wO<>sn=z66r;CFZl&%9hFzlk11;Y7UyDNo6u<0)Gp7foXgex1z~SS
zduRIeg(shN@-Ad=kX3Z7xYFmaWf4JqgLyBHsVw2>nZK>tdTDy29{0|6Z&w(3RE7K-
NC{{oyqh&{2-oN|I5%2&2