Bug 1509302 - Update webrender to commit 29dab25c587b38ecbf4713cda44e87b78846c7f6 (WR PR #3336). r=kats
authorWR Updater Bot <graphics-team@mozilla.staktrace.com>
Thu, 22 Nov 2018 18:48:47 +0000
changeset 504178 76fb27eae36a4c9b9c01b2a71de2b535cf3d3643
parent 504162 522388759018a97cce929697edef86f5f72a7caf
child 504179 5250566855534d4afa64e6ad50878724365e8103
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
bugs1509302
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 1509302 - Update webrender to commit 29dab25c587b38ecbf4713cda44e87b78846c7f6 (WR PR #3336). r=kats https://github.com/servo/webrender/pull/3336 Differential Revision: https://phabricator.services.mozilla.com/D12696
gfx/webrender/src/batch.rs
gfx/webrender/src/border.rs
gfx/webrender/src/display_list_flattener.rs
gfx/webrender/src/prim_store.rs
gfx/webrender/src/storage.rs
gfx/webrender_bindings/revision.txt
--- a/gfx/webrender/src/batch.rs
+++ b/gfx/webrender/src/batch.rs
@@ -11,17 +11,17 @@ use glyph_rasterizer::GlyphFormat;
 use gpu_cache::{GpuCache, GpuCacheHandle, GpuCacheAddress};
 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};
-use prim_store::{EdgeAaSegmentMask, ImageSource, PrimitiveInstanceKind};
+use prim_store::{EdgeAaSegmentMask, ImageSource, PrimitiveInstanceKind, PrimitiveStore};
 use prim_store::{VisibleGradientTile, PrimitiveInstance, PrimitiveOpacity};
 use prim_store::{BrushSegment, BorderSource, 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;
@@ -1256,25 +1256,27 @@ impl AlphaBatchBuilder {
                         };
 
                         if prim_instance.is_chased() {
                             println!("\ttask target {:?}", self.target_rect);
                             println!("\t{:?}", prim_header);
                         }
 
                         match brush.kind {
-                            BrushKind::Image { alpha_type, request, ref opacity_binding, ref visible_tiles, .. } if !visible_tiles.is_empty() => {
+                            BrushKind::Image { alpha_type, request, opacity_binding_index, ref visible_tiles, .. } if !visible_tiles.is_empty() => {
+                                let opacity_binding = ctx.prim_store.get_opacity_binding(opacity_binding_index);
+
                                 for tile in visible_tiles {
                                     if let Some((batch_kind, textures, user_data, uv_rect_address)) = get_image_tile_params(
                                             ctx.resource_cache,
                                             gpu_cache,
                                             deferred_resolves,
                                             request.with_tile(tile.tile_offset),
                                             alpha_type,
-                                            get_shader_opacity(opacity_binding.current),
+                                            get_shader_opacity(opacity_binding),
                                     ) {
                                         let prim_cache_address = gpu_cache.get_address(&tile.handle);
                                         let prim_header = PrimitiveHeader {
                                             specific_prim_address: prim_cache_address,
                                             local_rect: tile.local_rect,
                                             local_clip_rect: tile.local_clip_rect,
                                             ..prim_header
                                         };
@@ -1325,16 +1327,17 @@ impl AlphaBatchBuilder {
                                 );
                             }
                             _ => {
                                 if let Some(params) = brush.get_batch_params(
                                     ctx.resource_cache,
                                     gpu_cache,
                                     deferred_resolves,
                                     prim_instance,
+                                    ctx.prim_store,
                                 ) {
                                     let prim_header_index = prim_headers.push(&prim_header, z_id, params.prim_user_data);
                                     if prim_instance.is_chased() {
                                         println!("\t{:?} {:?}, task relative bounds {:?}",
                                             params.batch_kind, prim_header_index, bounding_rect);
                                     }
 
                                     self.add_brush_to_batch(
@@ -1686,19 +1689,20 @@ impl BrushBatchParameters {
 
 impl BrushPrimitive {
     fn get_batch_params(
         &self,
         resource_cache: &ResourceCache,
         gpu_cache: &mut GpuCache,
         deferred_resolves: &mut Vec<DeferredResolve>,
         prim_instance: &PrimitiveInstance,
+        prim_store: &PrimitiveStore,
     ) -> Option<BrushBatchParameters> {
         match self.kind {
-            BrushKind::Image { alpha_type, request, ref source, ref opacity_binding, .. } => {
+            BrushKind::Image { alpha_type, request, ref source, opacity_binding_index, .. } => {
                 let cache_item = match *source {
                     ImageSource::Default => {
                         resolve_image(
                             request,
                             resource_cache,
                             gpu_cache,
                             deferred_resolves,
                         )
@@ -1715,24 +1719,25 @@ impl BrushPrimitive {
                 if prim_instance.is_chased() {
                     println!("\tsource {:?}", cache_item);
                 }
 
                 if cache_item.texture_id == TextureSource::Invalid {
                     None
                 } else {
                     let textures = BatchTextures::color(cache_item.texture_id);
+                    let opacity_binding = prim_store.get_opacity_binding(opacity_binding_index);
 
                     Some(BrushBatchParameters::shared(
                         BrushBatchKind::Image(get_buffer_kind(cache_item.texture_id)),
                         textures,
                         [
                             ShaderColorMode::Image as i32 | ((alpha_type as i32) << 16),
                             RasterizationSpace::Local as i32,
-                            get_shader_opacity(opacity_binding.current),
+                            get_shader_opacity(opacity_binding),
                         ],
                         cache_item.uv_rect_handle.as_int(gpu_cache),
                     ))
                 }
             }
             BrushKind::Border { ref source, .. } => {
                 match *source {
                     BorderSource::Image(request) => {
@@ -1786,21 +1791,22 @@ impl BrushPrimitive {
                                 RasterizationSpace::Local as i32,
                                 get_shader_opacity(1.0),
                             ],
                             segment_data,
                         ))
                     }
                 }
             }
-            BrushKind::Solid { ref opacity_binding, .. } => {
+            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.current), 0, 0],
+                    [get_shader_opacity(opacity_binding), 0, 0],
                     0,
                 ))
             }
             BrushKind::RadialGradient { ref stops_handle, .. } => {
                 Some(BrushBatchParameters::shared(
                     BrushBatchKind::RadialGradient,
                     BatchTextures::no_texture(),
                     [
--- a/gfx/webrender/src/border.rs
+++ b/gfx/webrender/src/border.rs
@@ -8,17 +8,16 @@ use api::{DeviceVector2D, DevicePoint, L
 use api::{AuHelpers, NinePatchBorder, LayoutPoint, RepeatMode, TexelRect};
 use ellipse::Ellipse;
 use euclid::vec2;
 use display_list_flattener::DisplayListFlattener;
 use gpu_types::{BorderInstance, BorderSegment, BrushFlags};
 use prim_store::{BorderSegmentInfo, BrushKind, BrushPrimitive, BrushSegment, BrushSegmentVec};
 use prim_store::{EdgeAaSegmentMask, PrimitiveContainer, ScrollNodeAndClipChain, BrushSegmentDescriptor};
 use render_task::{RenderTaskCacheKey, RenderTaskCacheKeyKind};
-use smallvec::SmallVec;
 use util::{lerp, RectHelpers};
 
 // Using 2048 as the maximum radius in device space before which we
 // start stretching is up for debate.
 // the value must be chosen so that the corners will not use an
 // unreasonable amount of memory but should allow crisp corners in the
 // common cases.
 
@@ -601,17 +600,17 @@ fn get_edge_info(
 }
 
 /// Create the set of border segments and render task
 /// cache keys for a given CSS border.
 fn create_border_segments(
     rect: &LayoutRect,
     border: &NormalBorder,
     widths: &LayoutSideOffsets,
-    border_segments: &mut SmallVec<[BorderSegmentInfo; 8]>,
+    border_segments: &mut Vec<BorderSegmentInfo>,
     brush_segments: &mut BrushSegmentVec,
 ) {
     let local_size_tl = LayoutSize::new(
         border.radius.top_left.width.max(widths.left),
         border.radius.top_left.height.max(widths.top),
     );
     let local_size_tr = LayoutSize::new(
         border.radius.top_right.width.max(widths.right),
@@ -933,17 +932,17 @@ fn add_corner_segment(
     image_rect: LayoutRect,
     side0: BorderSide,
     side1: BorderSide,
     widths: LayoutSize,
     radius: LayoutSize,
     segment: BorderSegment,
     edge_flags: EdgeAaSegmentMask,
     brush_segments: &mut BrushSegmentVec,
-    border_segments: &mut SmallVec<[BorderSegmentInfo; 8]>,
+    border_segments: &mut Vec<BorderSegmentInfo>,
     do_aa: bool,
 ) {
     if side0.color.a <= 0.0 && side1.color.a <= 0.0 {
         return;
     }
 
     if widths.width <= 0.0 && widths.height <= 0.0 {
         return;
@@ -991,17 +990,17 @@ fn add_corner_segment(
 fn add_edge_segment(
     image_rect: LayoutRect,
     edge_info: &EdgeInfo,
     side: BorderSide,
     width: f32,
     segment: BorderSegment,
     edge_flags: EdgeAaSegmentMask,
     brush_segments: &mut BrushSegmentVec,
-    border_segments: &mut SmallVec<[BorderSegmentInfo; 8]>,
+    border_segments: &mut Vec<BorderSegmentInfo>,
     do_aa: bool,
 ) {
     if side.color.a <= 0.0 {
         return;
     }
 
     if side.style.is_hidden() {
         return;
@@ -1116,17 +1115,17 @@ pub fn build_border_instances(
 }
 
 pub fn create_normal_border_prim(
     local_rect: &LayoutRect,
     border: NormalBorder,
     widths: LayoutSideOffsets,
 ) -> BrushPrimitive {
     let mut brush_segments = BrushSegmentVec::new();
-    let mut border_segments = SmallVec::new();
+    let mut border_segments = Vec::new();
 
     create_border_segments(
         local_rect,
         &border,
         &widths,
         &mut border_segments,
         &mut brush_segments,
     );
--- a/gfx/webrender/src/display_list_flattener.rs
+++ b/gfx/webrender/src/display_list_flattener.rs
@@ -21,17 +21,17 @@ use glyph_rasterizer::FontInstance;
 use gpu_cache::GpuCacheHandle;
 use hit_test::{HitTestingItem, HitTestingRun};
 use image::simplify_repeated_primitive;
 use internal_types::{FastHashMap, FastHashSet};
 use picture::{Picture3DContext, PictureCompositeMode, PicturePrimitive, PrimitiveList};
 use prim_store::{BrushKind, BrushPrimitive, PrimitiveInstance, PrimitiveDataInterner, PrimitiveKeyKind};
 use prim_store::{ImageSource, PrimitiveOpacity, PrimitiveKey, PrimitiveSceneData, PrimitiveInstanceKind};
 use prim_store::{BorderSource, PrimitiveContainer, PrimitiveDataHandle, PrimitiveStore, PrimitiveStoreStats};
-use prim_store::{OpacityBinding, ScrollNodeAndClipChain, PictureIndex, register_prim_chase_id};
+use prim_store::{ScrollNodeAndClipChain, PictureIndex, register_prim_chase_id, OpacityBindingIndex};
 use render_backend::{DocumentView};
 use resource_cache::{FontInstanceMap, ImageRequest};
 use scene::{Scene, ScenePipeline, StackingContextHelpers};
 use scene_builder::DocumentResources;
 use spatial_node::{StickyFrameInfo};
 use std::{f32, mem};
 use std::collections::vec_deque::VecDeque;
 use tiling::{CompositeOps};
@@ -2010,17 +2010,17 @@ impl<'a> DisplayListFlattener<'a> {
                 },
                 alpha_type,
                 stretch_size,
                 tile_spacing,
                 color,
                 source: ImageSource::Default,
                 sub_rect,
                 visible_tiles: Vec::new(),
-                opacity_binding: OpacityBinding::new(),
+                opacity_binding_index: OpacityBindingIndex::INVALID,
             },
             None,
         );
 
         self.add_primitive(
             clip_and_scroll,
             &info,
             Vec::new(),
--- a/gfx/webrender/src/prim_store.rs
+++ b/gfx/webrender/src/prim_store.rs
@@ -676,36 +676,36 @@ pub struct BorderSegmentInfo {
     pub local_task_size: LayoutSize,
     pub cache_key: RenderTaskCacheKey,
 }
 
 #[derive(Debug)]
 pub enum BorderSource {
     Image(ImageRequest),
     Border {
-        segments: SmallVec<[BorderSegmentInfo; 8]>,
+        segments: Vec<BorderSegmentInfo>,
         border: NormalBorder,
         widths: LayoutSideOffsets,
     },
 }
 
 pub enum BrushKind {
     Solid {
         color: ColorF,
-        opacity_binding: OpacityBinding,
+        opacity_binding_index: OpacityBindingIndex,
     },
     Image {
         request: ImageRequest,
         alpha_type: AlphaType,
         stretch_size: LayoutSize,
         tile_spacing: LayoutSize,
         color: ColorF,
         source: ImageSource,
         sub_rect: Option<DeviceIntRect>,
-        opacity_binding: OpacityBinding,
+        opacity_binding_index: OpacityBindingIndex,
         visible_tiles: Vec<VisibleImageTile>,
     },
     YuvImage {
         yuv_key: [ImageKey; 3],
         format: YuvFormat,
         color_depth: ColorDepth,
         color_space: YuvColorSpace,
         image_rendering: ImageRendering,
@@ -757,26 +757,26 @@ impl BrushKind {
             BrushKind::LinearGradient { .. } => true,
         }
     }
 
     // Construct a brush that is a solid color rectangle.
     pub fn new_solid(color: ColorF) -> BrushKind {
         BrushKind::Solid {
             color,
-            opacity_binding: OpacityBinding::new(),
+            opacity_binding_index: OpacityBindingIndex::INVALID,
         }
     }
 
     // Construct a brush that is a border with `border` style and `widths`
     // dimensions.
     pub fn new_border(
         mut border: NormalBorder,
         widths: LayoutSideOffsets,
-        segments: SmallVec<[BorderSegmentInfo; 8]>,
+        segments: Vec<BorderSegmentInfo>,
     ) -> BrushKind {
         // FIXME(emilio): Is this the best place to do this?
         border.normalize(&widths);
 
         BrushKind::Border {
             source: BorderSource::Border {
                 border,
                 widths,
@@ -795,17 +795,17 @@ impl BrushKind {
         BrushKind::Image {
             request,
             alpha_type: AlphaType::PremultipliedAlpha,
             stretch_size,
             tile_spacing: LayoutSize::new(0., 0.),
             color,
             source: ImageSource::Default,
             sub_rect: None,
-            opacity_binding: OpacityBinding::new(),
+            opacity_binding_index: OpacityBindingIndex::INVALID,
             visible_tiles: Vec::new(),
         }
     }
 }
 
 bitflags! {
     /// Each bit of the edge AA mask is:
     /// 0, when the edge of the primitive needs to be considered for AA
@@ -1906,16 +1906,18 @@ impl PrimitiveInstance {
     pub fn is_chased(&self) -> bool {
         false
     }
 }
 
 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>;
 
 /// 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.
@@ -1950,48 +1952,55 @@ impl PrimitiveScratchBuffer {
 
 #[cfg_attr(feature = "capture", derive(Serialize))]
 #[cfg_attr(feature = "replay", derive(Deserialize))]
 #[derive(Clone, Debug)]
 pub struct PrimitiveStoreStats {
     primitive_count: usize,
     picture_count: usize,
     text_run_count: usize,
+    opacity_binding_count: usize,
 }
 
 impl PrimitiveStoreStats {
     pub fn empty() -> Self {
         PrimitiveStoreStats {
             primitive_count: 0,
             picture_count: 0,
             text_run_count: 0,
+            opacity_binding_count: 0,
         }
     }
 }
 
 pub struct PrimitiveStore {
     pub primitives: Vec<Primitive>,
     pub pictures: Vec<PicturePrimitive>,
     pub text_runs: TextRunStorage,
+
+    /// List of animated opacity bindings for a primitive.
+    pub opacity_bindings: OpacityBindingStorage,
 }
 
 impl PrimitiveStore {
     pub fn new(stats: &PrimitiveStoreStats) -> PrimitiveStore {
         PrimitiveStore {
             primitives: Vec::with_capacity(stats.primitive_count),
             pictures: Vec::with_capacity(stats.picture_count),
             text_runs: TextRunStorage::new(stats.text_run_count),
+            opacity_bindings: OpacityBindingStorage::new(stats.opacity_binding_count),
         }
     }
 
     pub fn get_stats(&self) -> PrimitiveStoreStats {
         PrimitiveStoreStats {
             primitive_count: self.primitives.len(),
             picture_count: self.pictures.len(),
             text_run_count: self.text_runs.len(),
+            opacity_binding_count: self.opacity_bindings.len(),
         }
     }
 
     pub fn create_picture(
         &mut self,
         prim: PicturePrimitive,
     ) -> PictureIndex {
         let index = PictureIndex(self.pictures.len());
@@ -2042,16 +2051,27 @@ impl PrimitiveStore {
             details,
         };
 
         self.primitives.push(prim);
 
         PrimitiveIndex(prim_index)
     }
 
+    pub fn get_opacity_binding(
+        &self,
+        opacity_binding_index: OpacityBindingIndex,
+    ) -> f32 {
+        if opacity_binding_index == OpacityBindingIndex::INVALID {
+            1.0
+        } else {
+            self.opacity_bindings[opacity_binding_index].current
+        }
+    }
+
     // 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> {
         let pic = &self.pictures[pic_index.0];
 
@@ -2087,17 +2107,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 { .. } => {
+                            BrushKind::Solid { .. } |
+                            BrushKind::Image { .. } => {
                                 return Some(prim_index)
                             }
                             BrushKind::Border { .. } |
                             BrushKind::YuvImage { .. } |
                             BrushKind::LinearGradient { .. } |
                             BrushKind::RadialGradient { .. } => {}
                         }
                     }
@@ -2131,18 +2152,22 @@ impl PrimitiveStore {
         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, .. } |
-                            BrushKind::Image { ref mut opacity_binding, .. } => {
+                            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());
+                                }
+                                let opacity_binding = &mut self.opacity_bindings[*opacity_binding_index];
                                 opacity_binding.push(binding);
                             }
                             BrushKind::YuvImage { .. } |
                             BrushKind::Border { .. } |
                             BrushKind::LinearGradient { .. } |
                             BrushKind::RadialGradient { .. } => {
                                 unreachable!("bug: invalid prim type for opacity collapse");
                             }
@@ -2454,16 +2479,17 @@ impl PrimitiveStore {
                     prim_local_rect,
                     prim_details,
                     prim_context,
                     pic_context.surface_index,
                     pic_state,
                     frame_context,
                     frame_state,
                     display_list,
+                    &mut self.opacity_bindings,
                 );
             }
         }
 
         true
     }
 
     pub fn prepare_primitives(
@@ -3065,16 +3091,17 @@ impl PrimitiveInstance {
         prim_local_rect: LayoutRect,
         prim_details: &mut PrimitiveDetails,
         prim_context: &PrimitiveContext,
         surface_index: SurfaceIndex,
         pic_state: &mut PictureState,
         frame_context: &FrameBuildingContext,
         frame_state: &mut FrameBuildingState,
         display_list: &BuiltDisplayList,
+        opacity_bindings: &mut OpacityBindingStorage,
     ) {
         let mut is_tiled = false;
 
         pic_state.is_cacheable &= self.is_cacheable(
             prim_details,
             frame_state.resource_cache,
         );
 
@@ -3083,29 +3110,33 @@ impl PrimitiveInstance {
                 brush.opacity = match brush.kind {
                     BrushKind::Image {
                         request,
                         sub_rect,
                         stretch_size,
                         color,
                         ref mut tile_spacing,
                         ref mut source,
-                        ref mut opacity_binding,
+                        opacity_binding_index,
                         ref mut visible_tiles,
                         ..
                     } => {
                         let image_properties = frame_state
                             .resource_cache
                             .get_image_properties(request.key);
 
 
                         // Set if we need to request the source image from the cache this frame.
                         if let Some(image_properties) = image_properties {
                             is_tiled = image_properties.tiling.is_some();
-                            opacity_binding.update(frame_context.scene_properties);
+                            let current_opacity = update_opacity_binding(
+                                opacity_bindings,
+                                opacity_binding_index,
+                                frame_context.scene_properties,
+                            );
 
                             if *tile_spacing != LayoutSize::zero() && !is_tiled {
                                 *source = ImageSource::Cache {
                                     // Size in device-pixels we need to allocate in render task cache.
                                     size: image_properties.descriptor.size.to_i32(),
                                     handle: None,
                                 };
                             }
@@ -3279,17 +3310,17 @@ impl PrimitiveInstance {
                             } else if request_source_image {
                                 frame_state.resource_cache.request_image(
                                     request,
                                     frame_state.gpu_cache,
                                 );
                             }
 
                             if is_opaque {
-                                PrimitiveOpacity::from_alpha(opacity_binding.current * color.a)
+                                PrimitiveOpacity::from_alpha(current_opacity * color.a)
                             } else {
                                 PrimitiveOpacity::translucent()
                             }
                         } else {
                             PrimitiveOpacity::opaque()
                         }
                     }
                     BrushKind::YuvImage { format, yuv_key, image_rendering, .. } => {
@@ -3488,19 +3519,23 @@ 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, ref mut opacity_binding, .. } => {
-                        opacity_binding.update(frame_context.scene_properties);
-                        PrimitiveOpacity::from_alpha(opacity_binding.current * color.a)
+                    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;
@@ -3663,8 +3698,22 @@ fn get_line_decoration_sizes(
             let slope_length = h - line_thickness;
             let flat_length = ((line_thickness - 1.0) * 2.0).max(1.0);
             let approx_period = 2.0 * (slope_length + flat_length);
 
             Some((approx_period, h))
         }
     }
 }
+
+fn update_opacity_binding(
+    opacity_bindings: &mut OpacityBindingStorage,
+    opacity_binding_index: OpacityBindingIndex,
+    scene_properties: &SceneProperties,
+) -> f32 {
+    if opacity_binding_index == OpacityBindingIndex::INVALID {
+        1.0
+    } else {
+        let binding = &mut opacity_bindings[opacity_binding_index];
+        binding.update(scene_properties);
+        binding.current
+    }
+}
--- a/gfx/webrender/src/storage.rs
+++ b/gfx/webrender/src/storage.rs
@@ -1,32 +1,41 @@
 /* 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/. */
 
-use std::{iter::Extend, ops, marker::PhantomData};
+use std::{iter::Extend, ops, marker::PhantomData, u32};
 use util::recycle_vec;
 
 #[derive(Debug, Hash)]
 #[cfg_attr(feature = "capture", derive(Serialize))]
 #[cfg_attr(feature = "replay", derive(Deserialize))]
 pub struct Index<T>(u32, PhantomData<T>);
 
 // We explicitly implement Copy + Clone instead of using #[derive(Copy, Clone)]
 // because we don't want to require that T implements Clone + Copy.
 impl<T> Clone for Index<T> {
     fn clone(&self) -> Self { *self }
 }
+
 impl<T> Copy for Index<T> {}
 
+impl<T> PartialEq for Index<T> {
+    fn eq(&self, other: &Self) -> bool {
+        self.0 == other.0
+    }
+}
+
 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);
 }
 
 #[derive(Debug)]
 pub struct Range<T> {
     pub start: Index<T>,
     pub end: Index<T>,
 }
 
--- a/gfx/webrender_bindings/revision.txt
+++ b/gfx/webrender_bindings/revision.txt
@@ -1,1 +1,1 @@
-3d7a8fa933769b94875f822b6f4a7803da4320ee
+29dab25c587b38ecbf4713cda44e87b78846c7f6