Bug 1515349 - Update webrender to commit 34411999ff1d72b91256c18aec3738e34d9922bb (WR PR #3437). r=kats
authorWR Updater Bot <graphics-team@mozilla.staktrace.com>
Wed, 19 Dec 2018 18:23:43 +0000
changeset 508446 f694f7ec747ccf99ac0016022129d824bd311614
parent 508445 ef9d941b10a8ef56fd81ba86f43b2ac60ffbf6ac
child 508447 c1c4159948700d2a8b94d523fffc819e67720bc8
push id10547
push userffxbld-merge
push dateMon, 21 Jan 2019 13:03:58 +0000
treeherdermozilla-beta@24ec1916bffe [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewerskats
bugs1515349
milestone66.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 1515349 - Update webrender to commit 34411999ff1d72b91256c18aec3738e34d9922bb (WR PR #3437). r=kats https://github.com/servo/webrender/pull/3437 Differential Revision: https://phabricator.services.mozilla.com/D14973
gfx/webrender_bindings/revision.txt
gfx/wr/webrender/src/batch.rs
gfx/wr/webrender/src/display_list_flattener.rs
gfx/wr/webrender/src/picture.rs
gfx/wr/webrender/src/prim_store/line_dec.rs
gfx/wr/webrender/src/prim_store/mod.rs
gfx/wr/webrender/src/prim_store/picture.rs
gfx/wr/webrender/src/profiler.rs
gfx/wr/webrender/src/render_backend.rs
gfx/wr/webrender/src/render_task.rs
gfx/wr/webrender/src/scene_builder.rs
--- a/gfx/webrender_bindings/revision.txt
+++ b/gfx/webrender_bindings/revision.txt
@@ -1,1 +1,1 @@
-6af87e7d8552d461b3744eb39ef0d6c618b5261a
+34411999ff1d72b91256c18aec3738e34d9922bb
--- a/gfx/wr/webrender/src/batch.rs
+++ b/gfx/wr/webrender/src/batch.rs
@@ -785,19 +785,18 @@ impl AlphaBatchBuilder {
                             ));
                         }
                     },
                 );
             }
             PrimitiveInstanceKind::LineDecoration { data_handle, ref cache_handle, .. } => {
                 // The GPU cache data is stored in the template and reused across
                 // frames and display lists.
-
-                let prim_data = &ctx.resources.prim_data_store[data_handle];
-                let prim_cache_address = gpu_cache.get_address(&prim_data.gpu_cache_handle);
+                let common_data = &ctx.resources.line_decoration_data_store[data_handle].common;
+                let prim_cache_address = gpu_cache.get_address(&common_data.gpu_cache_handle);
 
                 let (batch_kind, textures, prim_user_data, segment_user_data) = match cache_handle {
                     Some(cache_handle) => {
                         let rt_cache_entry = ctx
                             .resource_cache
                             .get_cached_render_task(cache_handle);
                         let cache_item = ctx
                             .resource_cache
@@ -822,17 +821,17 @@ impl AlphaBatchBuilder {
                             0,
                         )
                     }
                 };
 
                 // TODO(gw): We can abstract some of the common code below into
                 //           helper methods, as we port more primitives to make
                 //           use of interning.
-                let blend_mode = if !prim_data.opacity.is_opaque ||
+                let blend_mode = if !common_data.opacity.is_opaque ||
                     prim_instance.clip_task_index != ClipTaskIndex::INVALID ||
                     transform_kind == TransformedRectKind::Complex
                 {
                     BlendMode::PremultipliedAlpha
                 } else {
                     BlendMode::None
                 };
 
--- a/gfx/wr/webrender/src/display_list_flattener.rs
+++ b/gfx/wr/webrender/src/display_list_flattener.rs
@@ -17,23 +17,25 @@ use clip::{ClipChainId, ClipRegion, Clip
 use clip_scroll_tree::{ROOT_SPATIAL_NODE_INDEX, ClipScrollTree, SpatialNodeIndex};
 use frame_builder::{ChasePrimitive, FrameBuilder, FrameBuilderConfig};
 use glyph_rasterizer::FontInstance;
 use hit_test::{HitTestingItem, HitTestingRun};
 use image::simplify_repeated_primitive;
 use intern::{Handle, Internable, InternDebug};
 use internal_types::{FastHashMap, FastHashSet};
 use picture::{Picture3DContext, PictureCompositeMode, PicturePrimitive, PrimitiveList};
-use prim_store::{PrimitiveInstance, PrimitiveKeyKind, PictureCompositeKey};
-use prim_store::{PrimitiveKey, PrimitiveSceneData, PrimitiveInstanceKind, NinePatchDescriptor};
-use prim_store::{PrimitiveStore, PrimitiveStoreStats, LineDecorationCacheKey};
-use prim_store::{ScrollNodeAndClipChain, PictureIndex, register_prim_chase_id, get_line_decoration_sizes};
+use prim_store::{PrimitiveInstance, PrimitiveKeyKind, PrimitiveSceneData};
+use prim_store::{PrimitiveInstanceKind, NinePatchDescriptor, PrimitiveStore};
+use prim_store::{PrimitiveStoreStats, ScrollNodeAndClipChain, PictureIndex};
+use prim_store::{register_prim_chase_id, get_line_decoration_sizes};
 use prim_store::borders::{ImageBorder, NormalBorderPrim};
 use prim_store::gradient::{GradientStopKey, LinearGradient, RadialGradient, RadialGradientParams};
 use prim_store::image::{Image, YuvImage};
+use prim_store::line_dec::{LineDecoration, LineDecorationCacheKey};
+use prim_store::picture::{Picture, PictureCompositeKey, PictureKey};
 use prim_store::text_run::TextRun;
 use render_backend::{DocumentView};
 use resource_cache::{FontInstanceMap, ImageRequest};
 use scene::{Scene, ScenePipeline, StackingContextHelpers};
 use scene_builder::{DocumentResources, InternerMut};
 use spatial_node::{StickyFrameInfo, ScrollFrameKind, SpatialNodeType};
 use std::{f32, mem, usize};
 use std::collections::vec_deque::VecDeque;
@@ -323,28 +325,28 @@ impl<'a> DisplayListFlattener<'a> {
 
         let prim_list = PrimitiveList::new(
             remaining_prims,
             &self.resources,
         );
 
         // Now, create a picture with tile caching enabled that will hold all
         // of the primitives selected as belonging to the main scroll root.
-        let prim_key = PrimitiveKey::new(
+        let pic_key = PictureKey::new(
             true,
             LayoutSize::zero(),
             LayoutRect::max_rect(),
-            PrimitiveKeyKind::Picture {
+            Picture {
                 composite_mode_key: PictureCompositeKey::Identity,
             },
         );
 
-        let primitive_data_handle = self.resources
-            .prim_interner
-            .intern(&prim_key, || {
+        let pic_data_handle = self.resources
+            .picture_interner
+            .intern(&pic_key, || {
                 PrimitiveSceneData {
                     prim_relative_clip_rect: LayoutRect::max_rect(),
                     prim_size: LayoutSize::zero(),
                     is_backface_visible: true,
                 }
             }
         );
 
@@ -359,17 +361,17 @@ impl<'a> DisplayListFlattener<'a> {
             main_scroll_root,
             LayoutRect::max_rect(),
             &self.clip_store,
         ));
 
         let instance = PrimitiveInstance::new(
             LayoutPoint::zero(),
             PrimitiveInstanceKind::Picture {
-                data_handle: primitive_data_handle,
+                data_handle: pic_data_handle,
                 pic_index: PictureIndex(pic_index)
             },
             ClipChainId::NONE,
             main_scroll_root,
         );
 
         // This contains the tile caching picture, with preceding and
         // trailing primitives outside the main scroll root.
@@ -1820,16 +1822,19 @@ impl<'a> DisplayListFlattener<'a> {
                     // list to this shadow.
                     let mut prims = Vec::new();
 
                     for item in &items {
                         match item {
                             ShadowItem::Image(ref pending_image) => {
                                 self.add_shadow_prim(&pending_shadow, pending_image, &mut prims)
                             }
+                            ShadowItem::LineDecoration(ref pending_line_dec) => {
+                                self.add_shadow_prim(&pending_shadow, pending_line_dec, &mut prims)
+                            }
                             ShadowItem::NormalBorder(ref pending_border) => {
                                 self.add_shadow_prim(&pending_shadow, pending_border, &mut prims)
                             }
                             ShadowItem::Primitive(ref pending_primitive) => {
                                 self.add_shadow_prim(&pending_shadow, pending_primitive, &mut prims)
                             }
                             ShadowItem::TextRun(ref pending_text_run) => {
                                 self.add_shadow_prim(&pending_shadow, pending_text_run, &mut prims)
@@ -1864,28 +1869,26 @@ impl<'a> DisplayListFlattener<'a> {
                                     &self.resources,
                                 ),
                                 pending_shadow.clip_and_scroll.spatial_node_index,
                                 max_clip,
                                 &self.clip_store,
                             ))
                         );
 
-                        let shadow_prim_key = PrimitiveKey::new(
+                        let shadow_pic_key = PictureKey::new(
                             true,
                             LayoutSize::zero(),
                             LayoutRect::max_rect(),
-                            PrimitiveKeyKind::Picture {
-                                composite_mode_key,
-                            },
+                            Picture { composite_mode_key },
                         );
 
                         let shadow_prim_data_handle = self.resources
-                            .prim_interner
-                            .intern(&shadow_prim_key, || {
+                            .picture_interner
+                            .intern(&shadow_pic_key, || {
                                 PrimitiveSceneData {
                                     prim_relative_clip_rect: LayoutRect::max_rect(),
                                     prim_size: LayoutSize::zero(),
                                     is_backface_visible: true,
                                 }
                             }
                         );
 
@@ -1902,19 +1905,22 @@ impl<'a> DisplayListFlattener<'a> {
                         // Add the shadow primitive. This must be done before pushing this
                         // picture on to the shadow stack, to avoid infinite recursion!
                         self.add_primitive_to_draw_list(shadow_prim_instance);
                     }
                 }
                 ShadowItem::Image(pending_image) => {
                     self.add_shadow_prim_to_draw_list(pending_image)
                 },
+                ShadowItem::LineDecoration(pending_line_dec) => {
+                    self.add_shadow_prim_to_draw_list(pending_line_dec)
+                },
                 ShadowItem::NormalBorder(pending_border) => {
                     self.add_shadow_prim_to_draw_list(pending_border)
-                }
+                },
                 ShadowItem::Primitive(pending_primitive) => {
                     self.add_shadow_prim_to_draw_list(pending_primitive)
                 },
                 ShadowItem::TextRun(pending_text_run) => {
                     self.add_shadow_prim_to_draw_list(pending_text_run)
                 },
             }
         }
@@ -2087,17 +2093,17 @@ impl<'a> DisplayListFlattener<'a> {
                 size: size.to_au(),
             }
         });
 
         self.add_primitive(
             clip_and_scroll,
             &info,
             Vec::new(),
-            PrimitiveKeyKind::LineDecoration {
+            LineDecoration {
                 cache_key,
                 color: color.into(),
             },
         );
     }
 
     pub fn add_border(
         &mut self,
@@ -2678,27 +2684,34 @@ pub struct PendingPrimitive<T> {
 pub struct PendingShadow {
     shadow: Shadow,
     clip_and_scroll: ScrollNodeAndClipChain,
 }
 
 pub enum ShadowItem {
     Shadow(PendingShadow),
     Image(PendingPrimitive<Image>),
+    LineDecoration(PendingPrimitive<LineDecoration>),
     NormalBorder(PendingPrimitive<NormalBorderPrim>),
     Primitive(PendingPrimitive<PrimitiveKeyKind>),
     TextRun(PendingPrimitive<TextRun>),
 }
 
 impl From<PendingPrimitive<Image>> for ShadowItem {
     fn from(image: PendingPrimitive<Image>) -> Self {
         ShadowItem::Image(image)
     }
 }
 
+impl From<PendingPrimitive<LineDecoration>> for ShadowItem {
+    fn from(line_dec: PendingPrimitive<LineDecoration>) -> Self {
+        ShadowItem::LineDecoration(line_dec)
+    }
+}
+
 impl From<PendingPrimitive<NormalBorderPrim>> for ShadowItem {
     fn from(border: PendingPrimitive<NormalBorderPrim>) -> Self {
         ShadowItem::NormalBorder(border)
     }
 }
 
 impl From<PendingPrimitive<PrimitiveKeyKind>> for ShadowItem {
     fn from(container: PendingPrimitive<PrimitiveKeyKind>) -> Self {
@@ -2715,28 +2728,26 @@ impl From<PendingPrimitive<TextRun>> for
 fn create_prim_instance(
     pic_index: PictureIndex,
     composite_mode_key: PictureCompositeKey,
     is_backface_visible: bool,
     clip_chain_id: ClipChainId,
     spatial_node_index: SpatialNodeIndex,
     resources: &mut DocumentResources,
 ) -> PrimitiveInstance {
-    let prim_key = PrimitiveKey::new(
+    let pic_key = PictureKey::new(
         is_backface_visible,
         LayoutSize::zero(),
         LayoutRect::max_rect(),
-        PrimitiveKeyKind::Picture {
-            composite_mode_key,
-        },
+        Picture { composite_mode_key },
     );
 
     let data_handle = resources
-        .prim_interner
-        .intern(&prim_key, || {
+        .picture_interner
+        .intern(&pic_key, || {
             PrimitiveSceneData {
                 prim_relative_clip_rect: LayoutRect::max_rect(),
                 prim_size: LayoutSize::zero(),
                 is_backface_visible,
             }
         }
     );
 
--- a/gfx/wr/webrender/src/picture.rs
+++ b/gfx/wr/webrender/src/picture.rs
@@ -1387,34 +1387,38 @@ impl PrimitiveList {
                     true
                 }
                 _ => {
                     false
                 }
             };
 
             let prim_data = match prim_instance.kind {
-                PrimitiveInstanceKind::Picture { data_handle, .. } |
-                PrimitiveInstanceKind::LineDecoration { data_handle, .. } |
                 PrimitiveInstanceKind::Rectangle { data_handle, .. } |
                 PrimitiveInstanceKind::Clear { data_handle, .. } => {
                     &resources.prim_interner[data_handle]
                 }
                 PrimitiveInstanceKind::Image { data_handle, .. } => {
                     &resources.image_interner[data_handle]
                 }
                 PrimitiveInstanceKind::ImageBorder { data_handle, .. } => {
                     &resources.image_border_interner[data_handle]
                 }
+                PrimitiveInstanceKind::LineDecoration { data_handle, .. } => {
+                    &resources.line_decoration_interner[data_handle]
+                }
                 PrimitiveInstanceKind::LinearGradient { data_handle, .. } => {
                     &resources.linear_grad_interner[data_handle]
                 }
                 PrimitiveInstanceKind::NormalBorder { data_handle, .. } => {
                     &resources.normal_border_interner[data_handle]
                 }
+                PrimitiveInstanceKind::Picture { data_handle, .. } => {
+                    &resources.picture_interner[data_handle]
+                }
                 PrimitiveInstanceKind::RadialGradient { data_handle, ..} => {
                     &resources.radial_grad_interner[data_handle]
                 }
                 PrimitiveInstanceKind::TextRun { data_handle, .. } => {
                     &resources.text_run_interner[data_handle]
                 }
                 PrimitiveInstanceKind::YuvImage { data_handle, .. } => {
                     &resources.yuv_image_interner[data_handle]
new file mode 100644
--- /dev/null
+++ b/gfx/wr/webrender/src/prim_store/line_dec.rs
@@ -0,0 +1,195 @@
+/* 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 api::{
+    ColorF, ColorU, LayoutPrimitiveInfo, LayoutRect, LayoutSizeAu,
+    LineOrientation, LineStyle, PremultipliedColorF, Shadow,
+};
+use app_units::Au;
+use display_list_flattener::{AsInstanceKind, CreateShadow, IsVisible};
+use frame_builder::{FrameBuildingState};
+use gpu_cache::GpuDataRequest;
+use intern;
+use prim_store::{
+    PrimKey, PrimKeyCommonData, PrimTemplate, PrimTemplateCommonData,
+    PrimitiveSceneData, PrimitiveStore,
+};
+use prim_store::PrimitiveInstanceKind;
+
+#[derive(Clone, Debug, Hash, PartialEq, Eq)]
+#[cfg_attr(feature = "capture", derive(Serialize))]
+#[cfg_attr(feature = "replay", derive(Deserialize))]
+pub struct LineDecorationCacheKey {
+    pub style: LineStyle,
+    pub orientation: LineOrientation,
+    pub wavy_line_thickness: Au,
+    pub size: LayoutSizeAu,
+}
+
+/// Identifying key for a line decoration.
+#[derive(Clone, Debug, Hash, PartialEq, Eq)]
+#[cfg_attr(feature = "capture", derive(Serialize))]
+#[cfg_attr(feature = "replay", derive(Deserialize))]
+pub struct LineDecoration {
+    // If the cache_key is Some(..) it is a line decoration
+    // that relies on a render task (e.g. wavy). If the
+    // cache key is None, it uses a fast path to draw the
+    // line decoration as a solid rect.
+    pub cache_key: Option<LineDecorationCacheKey>,
+    pub color: ColorU,
+}
+
+pub type LineDecorationKey = PrimKey<LineDecoration>;
+
+impl LineDecorationKey {
+    pub fn new(
+        info: &LayoutPrimitiveInfo,
+        prim_relative_clip_rect: LayoutRect,
+        line_dec: LineDecoration,
+    ) -> Self {
+        LineDecorationKey {
+            common: PrimKeyCommonData::with_info(
+                info,
+                prim_relative_clip_rect,
+            ),
+            kind: line_dec,
+        }
+    }
+}
+
+impl intern::InternDebug for LineDecorationKey {}
+
+impl AsInstanceKind<LineDecorationDataHandle> for LineDecorationKey {
+    /// Construct a primitive instance that matches the type
+    /// of primitive key.
+    fn as_instance_kind(
+        &self,
+        data_handle: LineDecorationDataHandle,
+        _: &mut PrimitiveStore,
+    ) -> PrimitiveInstanceKind {
+        PrimitiveInstanceKind::LineDecoration {
+            data_handle,
+            cache_handle: None,
+        }
+    }
+}
+
+#[cfg_attr(feature = "capture", derive(Serialize))]
+#[cfg_attr(feature = "replay", derive(Deserialize))]
+pub struct LineDecorationData {
+    pub cache_key: Option<LineDecorationCacheKey>,
+    pub color: ColorF,
+}
+
+impl LineDecorationData {
+    /// Update the GPU cache for a given primitive template. This may be called multiple
+    /// times per frame, by each primitive reference that refers to this interned
+    /// template. The initial request call to the GPU cache ensures that work is only
+    /// done if the cache entry is invalid (due to first use or eviction).
+    pub fn update(
+        &mut self,
+        common: &mut PrimTemplateCommonData,
+        frame_state: &mut FrameBuildingState,
+    ) {
+        if let Some(ref mut request) = frame_state.gpu_cache.request(&mut common.gpu_cache_handle) {
+            self.write_prim_gpu_blocks(request);
+        }
+    }
+
+    fn write_prim_gpu_blocks(
+        &self,
+        request: &mut GpuDataRequest
+    ) {
+        match self.cache_key.as_ref() {
+            Some(cache_key) => {
+                request.push(self.color.premultiplied());
+                request.push(PremultipliedColorF::WHITE);
+                request.push([
+                    cache_key.size.width.to_f32_px(),
+                    cache_key.size.height.to_f32_px(),
+                    0.0,
+                    0.0,
+                ]);
+            }
+            None => {
+                request.push(self.color.premultiplied());
+            }
+        }
+    }
+}
+
+pub type LineDecorationTemplate = PrimTemplate<LineDecorationData>;
+
+impl From<LineDecorationKey> for LineDecorationTemplate {
+    fn from(line_dec: LineDecorationKey) -> Self {
+        let common = PrimTemplateCommonData::with_key_common(line_dec.common);
+        LineDecorationTemplate {
+            common,
+            kind: LineDecorationData {
+                cache_key: line_dec.kind.cache_key,
+                color: line_dec.kind.color.into(),
+            }
+        }
+    }
+}
+
+#[cfg_attr(feature = "capture", derive(Serialize))]
+#[cfg_attr(feature = "replay", derive(Deserialize))]
+#[derive(Clone, Copy, Debug, Hash, Eq, PartialEq)]
+pub struct LineDecorationDataMarker;
+
+pub type LineDecorationDataStore = intern::DataStore<LineDecorationKey, LineDecorationTemplate, LineDecorationDataMarker>;
+pub type LineDecorationDataHandle = intern::Handle<LineDecorationDataMarker>;
+pub type LineDecorationDataUpdateList = intern::UpdateList<LineDecorationKey>;
+pub type LineDecorationDataInterner = intern::Interner<LineDecorationKey, PrimitiveSceneData, LineDecorationDataMarker>;
+
+impl intern::Internable for LineDecoration {
+    type Marker = LineDecorationDataMarker;
+    type Source = LineDecorationKey;
+    type StoreData = LineDecorationTemplate;
+    type InternData = PrimitiveSceneData;
+
+    /// Build a new key from self with `info`.
+    fn build_key(
+        self,
+        info: &LayoutPrimitiveInfo,
+        prim_relative_clip_rect: LayoutRect,
+    ) -> LineDecorationKey {
+        LineDecorationKey::new(
+            info,
+            prim_relative_clip_rect,
+            self,
+        )
+    }
+}
+
+impl CreateShadow for LineDecoration {
+    fn create_shadow(&self, shadow: &Shadow) -> Self {
+        LineDecoration {
+            color: shadow.color.into(),
+            cache_key: self.cache_key.clone(),
+        }
+    }
+}
+
+impl IsVisible for LineDecoration {
+    fn is_visible(&self) -> bool {
+        self.color.a > 0
+    }
+}
+
+#[test]
+#[cfg(target_os = "linux")]
+fn test_struct_sizes() {
+    use std::mem;
+    // The sizes of these structures are critical for performance on a number of
+    // talos stress tests. If you get a failure here on CI, there's two possibilities:
+    // (a) You made a structure smaller than it currently is. Great work! Update the
+    //     test expectations and move on.
+    // (b) You made a structure larger. This is not necessarily a problem, but should only
+    //     be done with care, and after checking if talos performance regresses badly.
+    assert_eq!(mem::size_of::<LineDecoration>(), 20, "LineDecoration size changed");
+    assert_eq!(mem::size_of::<LineDecorationTemplate>(), 88, "LineDecorationTemplate size changed");
+    assert_eq!(mem::size_of::<LineDecorationKey>(), 48, "LineDecorationKey size changed");
+}
--- a/gfx/wr/webrender/src/prim_store/mod.rs
+++ b/gfx/wr/webrender/src/prim_store/mod.rs
@@ -1,21 +1,20 @@
 /* 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 api::{BorderRadius, ClipMode, ColorF, PictureRect, ColorU, LayoutVector2D};
 use api::{DeviceIntRect, DevicePixelScale, DeviceRect};
-use api::{FilterOp, ImageRendering, TileOffset, RepeatMode, MixBlendMode};
-use api::{LayoutPoint, LayoutRect, LayoutSideOffsets, LayoutSize, PropertyBindingId};
+use api::{FilterOp, ImageRendering, TileOffset, RepeatMode};
+use api::{LayoutPoint, LayoutRect, LayoutSideOffsets, LayoutSize};
 use api::{PremultipliedColorF, PropertyBinding, Shadow};
 use api::{WorldPixel, BoxShadowClipMode, WorldRect, LayoutToWorldScale};
-use api::{PicturePixel, RasterPixel, LineStyle, LineOrientation, LayoutSizeAu, AuHelpers};
+use api::{PicturePixel, RasterPixel, LineStyle, LineOrientation, AuHelpers};
 use api::LayoutPrimitiveInfo;
-use app_units::Au;
 use border::{get_max_scale_for_border, build_border_instances};
 use border::BorderSegmentCacheKey;
 use clip::{ClipStore};
 use clip_scroll_tree::{ClipScrollTree, SpatialNodeIndex};
 use clip::{ClipDataStore, ClipNodeFlags, ClipChainId, ClipChainInstance, ClipItem, ClipNodeCollector};
 use display_list_flattener::{AsInstanceKind, CreateShadow, IsVisible};
 use euclid::{SideOffsets2D, TypedTransform3D, TypedRect, TypedScale, TypedSize2D};
 use frame_builder::{FrameBuildingContext, FrameBuildingState, PictureContext, PictureState};
@@ -25,16 +24,18 @@ use gpu_cache::{GpuCache, GpuCacheAddres
 use gpu_types::BrushFlags;
 use image::{Repetition};
 use intern;
 use picture::{PictureCompositeMode, PicturePrimitive, PictureUpdateState, TileCacheUpdateState};
 use picture::{ClusterIndex, PrimitiveList, SurfaceIndex, SurfaceInfo, RetainedTiles, RasterConfig};
 use prim_store::borders::{ImageBorderDataHandle, NormalBorderDataHandle};
 use prim_store::gradient::{LinearGradientDataHandle, RadialGradientDataHandle};
 use prim_store::image::{ImageDataHandle, ImageInstance, VisibleImageTile, YuvImageDataHandle};
+use prim_store::line_dec::LineDecorationDataHandle;
+use prim_store::picture::PictureDataHandle;
 use prim_store::text_run::{TextRunDataHandle, TextRunPrimitive};
 #[cfg(debug_assertions)]
 use render_backend::{FrameId};
 use render_backend::FrameResources;
 use render_task::{RenderTask, RenderTaskCacheKey, to_cache_size};
 use render_task::{RenderTaskCacheKeyKind, RenderTaskId, RenderTaskCacheEntryHandle};
 use renderer::{MAX_VERTEX_TEXTURE_WIDTH};
 use resource_cache::{ImageProperties, ImageRequest, ResourceCache};
@@ -46,16 +47,18 @@ use std::sync::atomic::{AtomicUsize, Ord
 use storage;
 use util::{ScaleOffset, MatrixHelpers, MaxRect, recycle_vec};
 use util::{pack_as_float, project_rect, raster_rect_to_device_pixels};
 use smallvec::SmallVec;
 
 pub mod borders;
 pub mod gradient;
 pub mod image;
+pub mod line_dec;
+pub mod picture;
 pub mod text_run;
 
 /// Counter for unique primitive IDs for debug tracing.
 #[cfg(debug_assertions)]
 static NEXT_PRIM_ID: AtomicUsize = AtomicUsize::new(0);
 
 #[cfg(debug_assertions)]
 static PRIM_CHASE_ID: AtomicUsize = AtomicUsize::new(usize::MAX);
@@ -341,149 +344,27 @@ impl GpuCacheAddress {
 #[cfg_attr(feature = "capture", derive(Serialize))]
 #[cfg_attr(feature = "replay", derive(Deserialize))]
 pub struct PrimitiveSceneData {
     pub prim_size: LayoutSize,
     pub prim_relative_clip_rect: LayoutRect,
     pub is_backface_visible: bool,
 }
 
-/// Represents a hashable description of how a picture primitive
-/// will be composited into its parent.
-#[cfg_attr(feature = "capture", derive(Serialize))]
-#[cfg_attr(feature = "replay", derive(Deserialize))]
-#[derive(Debug, Clone, PartialEq, Hash, Eq)]
-pub enum PictureCompositeKey {
-    // No visual compositing effect
-    Identity,
-
-    // FilterOp
-    Blur(Au),
-    Brightness(Au),
-    Contrast(Au),
-    Grayscale(Au),
-    HueRotate(Au),
-    Invert(Au),
-    Opacity(Au),
-    OpacityBinding(PropertyBindingId, Au),
-    Saturate(Au),
-    Sepia(Au),
-    DropShadow(VectorKey, Au, ColorU),
-    ColorMatrix([Au; 20]),
-    SrgbToLinear,
-    LinearToSrgb,
-
-    // MixBlendMode
-    Multiply,
-    Screen,
-    Overlay,
-    Darken,
-    Lighten,
-    ColorDodge,
-    ColorBurn,
-    HardLight,
-    SoftLight,
-    Difference,
-    Exclusion,
-    Hue,
-    Saturation,
-    Color,
-    Luminosity,
-}
-
-impl From<Option<PictureCompositeMode>> for PictureCompositeKey {
-    fn from(mode: Option<PictureCompositeMode>) -> Self {
-        match mode {
-            Some(PictureCompositeMode::MixBlend(mode)) => {
-                match mode {
-                    MixBlendMode::Normal => PictureCompositeKey::Identity,
-                    MixBlendMode::Multiply => PictureCompositeKey::Multiply,
-                    MixBlendMode::Screen => PictureCompositeKey::Screen,
-                    MixBlendMode::Overlay => PictureCompositeKey::Overlay,
-                    MixBlendMode::Darken => PictureCompositeKey::Darken,
-                    MixBlendMode::Lighten => PictureCompositeKey::Lighten,
-                    MixBlendMode::ColorDodge => PictureCompositeKey::ColorDodge,
-                    MixBlendMode::ColorBurn => PictureCompositeKey::ColorBurn,
-                    MixBlendMode::HardLight => PictureCompositeKey::HardLight,
-                    MixBlendMode::SoftLight => PictureCompositeKey::SoftLight,
-                    MixBlendMode::Difference => PictureCompositeKey::Difference,
-                    MixBlendMode::Exclusion => PictureCompositeKey::Exclusion,
-                    MixBlendMode::Hue => PictureCompositeKey::Hue,
-                    MixBlendMode::Saturation => PictureCompositeKey::Saturation,
-                    MixBlendMode::Color => PictureCompositeKey::Color,
-                    MixBlendMode::Luminosity => PictureCompositeKey::Luminosity,
-                }
-            }
-            Some(PictureCompositeMode::Filter(op)) => {
-                match op {
-                    FilterOp::Blur(value) => PictureCompositeKey::Blur(Au::from_f32_px(value)),
-                    FilterOp::Brightness(value) => PictureCompositeKey::Brightness(Au::from_f32_px(value)),
-                    FilterOp::Contrast(value) => PictureCompositeKey::Contrast(Au::from_f32_px(value)),
-                    FilterOp::Grayscale(value) => PictureCompositeKey::Grayscale(Au::from_f32_px(value)),
-                    FilterOp::HueRotate(value) => PictureCompositeKey::HueRotate(Au::from_f32_px(value)),
-                    FilterOp::Invert(value) => PictureCompositeKey::Invert(Au::from_f32_px(value)),
-                    FilterOp::Saturate(value) => PictureCompositeKey::Saturate(Au::from_f32_px(value)),
-                    FilterOp::Sepia(value) => PictureCompositeKey::Sepia(Au::from_f32_px(value)),
-                    FilterOp::SrgbToLinear => PictureCompositeKey::SrgbToLinear,
-                    FilterOp::LinearToSrgb => PictureCompositeKey::LinearToSrgb,
-                    FilterOp::Identity => PictureCompositeKey::Identity,
-                    FilterOp::DropShadow(offset, radius, color) => {
-                        PictureCompositeKey::DropShadow(offset.into(), Au::from_f32_px(radius), color.into())
-                    }
-                    FilterOp::Opacity(binding, _) => {
-                        match binding {
-                            PropertyBinding::Value(value) => {
-                                PictureCompositeKey::Opacity(Au::from_f32_px(value))
-                            }
-                            PropertyBinding::Binding(key, default) => {
-                                PictureCompositeKey::OpacityBinding(key.id, Au::from_f32_px(default))
-                            }
-                        }
-                    }
-                    FilterOp::ColorMatrix(values) => {
-                        let mut quantized_values: [Au; 20] = [Au(0); 20];
-                        for (value, result) in values.iter().zip(quantized_values.iter_mut()) {
-                            *result = Au::from_f32_px(*value);
-                        }
-                        PictureCompositeKey::ColorMatrix(quantized_values)
-                    }
-                }
-            }
-            Some(PictureCompositeMode::Blit) |
-            Some(PictureCompositeMode::TileCache { .. }) |
-            None => {
-                PictureCompositeKey::Identity
-            }
-        }
-    }
-}
-
 /// Information specific to a primitive type that
 /// uniquely identifies a primitive template by key.
 #[cfg_attr(feature = "capture", derive(Serialize))]
 #[cfg_attr(feature = "replay", derive(Deserialize))]
 #[derive(Debug, Clone, Eq, PartialEq, Hash)]
 pub enum PrimitiveKeyKind {
-    /// Identifying key for a line decoration.
-    LineDecoration {
-        // If the cache_key is Some(..) it is a line decoration
-        // that relies on a render task (e.g. wavy). If the
-        // cache key is None, it uses a fast path to draw the
-        // line decoration as a solid rect.
-        cache_key: Option<LineDecorationCacheKey>,
-        color: ColorU,
-    },
     /// Clear an existing rect, used for special effects on some platforms.
     Clear,
     Rectangle {
         color: ColorU,
     },
-    Picture {
-        composite_mode_key: PictureCompositeKey,
-    },
 }
 
 #[cfg_attr(feature = "capture", derive(Serialize))]
 #[cfg_attr(feature = "replay", derive(Deserialize))]
 #[derive(Debug, Clone, PartialEq)]
 pub struct RectangleKey {
     x: f32,
     y: f32,
@@ -747,86 +628,57 @@ impl AsInstanceKind<PrimitiveDataHandle>
     /// Construct a primitive instance that matches the type
     /// of primitive key.
     fn as_instance_kind(
         &self,
         data_handle: PrimitiveDataHandle,
         _: &mut PrimitiveStore,
     ) -> PrimitiveInstanceKind {
         match self.kind {
-            PrimitiveKeyKind::LineDecoration { .. } => {
-                PrimitiveInstanceKind::LineDecoration {
-                    data_handle,
-                    cache_handle: None,
-                }
-            }
             PrimitiveKeyKind::Clear => {
                 PrimitiveInstanceKind::Clear {
                     data_handle
                 }
             }
             PrimitiveKeyKind::Rectangle { .. } => {
                 PrimitiveInstanceKind::Rectangle {
                     data_handle,
                     opacity_binding_index: OpacityBindingIndex::INVALID,
                     segment_instance_index: SegmentInstanceIndex::INVALID,
                 }
             }
-            PrimitiveKeyKind::Picture { .. } => {
-                // Should never be hit as this method should not be
-                // called for pictures.
-                unreachable!();
-            }
         }
     }
 }
 
 /// The shared information for a given primitive. This is interned and retained
 /// both across frames and display lists, by comparing the matching PrimitiveKey.
 #[cfg_attr(feature = "capture", derive(Serialize))]
 #[cfg_attr(feature = "replay", derive(Deserialize))]
 pub enum PrimitiveTemplateKind {
-    LineDecoration {
-        cache_key: Option<LineDecorationCacheKey>,
-        color: ColorF,
-    },
     Rectangle {
         color: ColorF,
     },
     Clear,
-    Picture {
-
-    },
 }
 
 /// 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) -> PrimitiveTemplateKind {
         match self {
-            PrimitiveKeyKind::Picture { .. } => {
-                PrimitiveTemplateKind::Picture {
-
-                }
-            }
             PrimitiveKeyKind::Clear => {
                 PrimitiveTemplateKind::Clear
             }
             PrimitiveKeyKind::Rectangle { color, .. } => {
                 PrimitiveTemplateKind::Rectangle {
                     color: color.into(),
                 }
             }
-            PrimitiveKeyKind::LineDecoration { cache_key, color } => {
-                PrimitiveTemplateKind::LineDecoration {
-                    cache_key,
-                    color: color.into(),
-                }
-            }
         }
     }
 }
 
 #[cfg_attr(feature = "capture", derive(Serialize))]
 #[cfg_attr(feature = "replay", derive(Deserialize))]
 pub struct PrimTemplateCommonData {
     pub is_backface_visible: bool,
@@ -897,34 +749,16 @@ impl PrimitiveTemplateKind {
         match *self {
             PrimitiveTemplateKind::Clear => {
                 // Opaque black with operator dest out
                 request.push(PremultipliedColorF::BLACK);
             }
             PrimitiveTemplateKind::Rectangle { ref color, .. } => {
                 request.push(color.premultiplied());
             }
-            PrimitiveTemplateKind::LineDecoration { ref cache_key, ref color } => {
-                match cache_key {
-                    Some(cache_key) => {
-                        request.push(color.premultiplied());
-                        request.push(PremultipliedColorF::WHITE);
-                        request.push([
-                            cache_key.size.width.to_f32_px(),
-                            cache_key.size.height.to_f32_px(),
-                            0.0,
-                            0.0,
-                        ]);
-                    }
-                    None => {
-                        request.push(color.premultiplied());
-                    }
-                }
-            }
-            PrimitiveTemplateKind::Picture { .. } => {}
         }
     }
 }
 
 impl PrimitiveTemplate {
     /// Update the GPU cache for a given primitive template. This may be called multiple
     /// times per frame, by each primitive reference that refers to this interned
     /// template. The initial request call to the GPU cache ensures that work is only
@@ -939,25 +773,16 @@ impl PrimitiveTemplate {
 
         self.opacity = match self.kind {
             PrimitiveTemplateKind::Clear => {
                 PrimitiveOpacity::translucent()
             }
             PrimitiveTemplateKind::Rectangle { ref color, .. } => {
                 PrimitiveOpacity::from_alpha(color.a)
             }
-            PrimitiveTemplateKind::LineDecoration { ref cache_key, ref color } => {
-                match cache_key {
-                    Some(..) => PrimitiveOpacity::translucent(),
-                    None => PrimitiveOpacity::from_alpha(color.a),
-                }
-            }
-            PrimitiveTemplateKind::Picture { .. } => {
-                PrimitiveOpacity::translucent()
-            }
         };
     }
 }
 
 // Type definitions for interning primitives.
 #[cfg_attr(feature = "capture", derive(Serialize))]
 #[cfg_attr(feature = "replay", derive(Deserialize))]
 #[derive(Clone, Copy, Debug, Hash, Eq, PartialEq)]
@@ -1157,26 +982,16 @@ impl BrushSegment {
             }
             None => {
                 ClipMaskKind::Clipped
             }
         }
     }
 }
 
-#[derive(Clone, Debug, Hash, PartialEq, Eq)]
-#[cfg_attr(feature = "capture", derive(Serialize))]
-#[cfg_attr(feature = "replay", derive(Deserialize))]
-pub struct LineDecorationCacheKey {
-    pub style: LineStyle,
-    pub orientation: LineOrientation,
-    pub wavy_line_thickness: Au,
-    pub size: LayoutSizeAu,
-}
-
 #[derive(Debug)]
 #[repr(C)]
 struct ClipRect {
     rect: LayoutRect,
     mode: f32,
 }
 
 #[derive(Debug)]
@@ -1400,49 +1215,40 @@ impl IsVisible for PrimitiveKeyKind {
     // 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
     //           we should move the logic for all other
     //           primitive types to use this.
     fn is_visible(&self) -> bool {
         match *self {
-            PrimitiveKeyKind::Clear |
-            PrimitiveKeyKind::Picture { .. } => {
+            PrimitiveKeyKind::Clear => {
                 true
             }
-            PrimitiveKeyKind::Rectangle { ref color, .. } |
-            PrimitiveKeyKind::LineDecoration { ref color, .. } => {
+            PrimitiveKeyKind::Rectangle { ref color, .. } => {
                 color.a > 0
             }
         }
     }
 }
 
 impl CreateShadow for PrimitiveKeyKind {
     // Create a clone of this PrimitiveContainer, applying whatever
     // changes are necessary to the primitive to support rendering
     // it as part of the supplied shadow.
     fn create_shadow(
         &self,
         shadow: &Shadow,
     ) -> PrimitiveKeyKind {
         match *self {
-            PrimitiveKeyKind::LineDecoration { ref cache_key, .. } => {
-                PrimitiveKeyKind::LineDecoration {
-                    color: shadow.color.into(),
-                    cache_key: cache_key.clone(),
-                }
-            }
             PrimitiveKeyKind::Rectangle { .. } => {
                 PrimitiveKeyKind::Rectangle {
                     color: shadow.color.into(),
                 }
             }
-            PrimitiveKeyKind::Picture { .. } |
             PrimitiveKeyKind::Clear => {
                 panic!("bug: this prim is not supported in shadow contexts");
             }
         }
     }
 }
 
 #[derive(Clone, Copy, Debug, PartialEq)]
@@ -1450,31 +1256,31 @@ impl CreateShadow for PrimitiveKeyKind {
 #[cfg_attr(feature = "replay", derive(Deserialize))]
 pub struct PrimitiveDebugId(pub usize);
 
 #[derive(Clone, Debug)]
 pub enum PrimitiveInstanceKind {
     /// Direct reference to a Picture
     Picture {
         /// Handle to the common interned data for this primitive.
-        data_handle: PrimitiveDataHandle,
+        data_handle: PictureDataHandle,
         pic_index: PictureIndex,
     },
     /// A run of glyphs, with associated font parameters.
     TextRun {
         /// Handle to the common interned data for this primitive.
         data_handle: TextRunDataHandle,
         /// Index to the per instance scratch data for this primitive.
         run_index: TextRunIndex,
     },
     /// A line decoration. cache_handle refers to a cached render
     /// task handle, if this line decoration is not a simple solid.
     LineDecoration {
         /// Handle to the common interned data for this primitive.
-        data_handle: PrimitiveDataHandle,
+        data_handle: LineDecorationDataHandle,
         // TODO(gw): For now, we need to store some information in
         //           the primitive instance that is created during
         //           prepare_prims and read during the batching pass.
         //           Once we unify the prepare_prims and batching to
         //           occur at the same time, we can remove most of
         //           the things we store here in the instance, and
         //           use them directly. This will remove cache_handle,
         //           but also the opacity, clip_task_id etc below.
@@ -1606,34 +1412,38 @@ impl PrimitiveInstance {
 
     #[cfg(not(debug_assertions))]
     pub fn is_chased(&self) -> bool {
         false
     }
 
     pub fn uid(&self) -> intern::ItemUid {
         match &self.kind {
-            PrimitiveInstanceKind::Picture { data_handle, .. } |
-            PrimitiveInstanceKind::LineDecoration { data_handle, .. } |
             PrimitiveInstanceKind::Clear { data_handle, .. } |
             PrimitiveInstanceKind::Rectangle { data_handle, .. } => {
                 data_handle.uid()
             }
             PrimitiveInstanceKind::Image { data_handle, .. } => {
                 data_handle.uid()
             }
             PrimitiveInstanceKind::ImageBorder { data_handle, .. } => {
                 data_handle.uid()
             }
+            PrimitiveInstanceKind::LineDecoration { data_handle, .. } => {
+                data_handle.uid()
+            }
             PrimitiveInstanceKind::LinearGradient { data_handle, .. } => {
                 data_handle.uid()
             }
             PrimitiveInstanceKind::NormalBorder { data_handle, .. } => {
                 data_handle.uid()
             }
+            PrimitiveInstanceKind::Picture { data_handle, .. } => {
+                data_handle.uid()
+            }
             PrimitiveInstanceKind::RadialGradient { data_handle, .. } => {
                 data_handle.uid()
             }
             PrimitiveInstanceKind::TextRun { data_handle, .. } => {
                 data_handle.uid()
             }
             PrimitiveInstanceKind::YuvImage { data_handle, .. } => {
                 data_handle.uid()
@@ -2451,68 +2261,65 @@ impl PrimitiveStore {
         frame_state: &mut FrameBuildingState,
         resources: &mut FrameResources,
         scratch: &mut PrimitiveScratchBuffer,
     ) {
         let is_chased = prim_instance.is_chased();
 
         match &mut prim_instance.kind {
             PrimitiveInstanceKind::LineDecoration { data_handle, ref mut cache_handle, .. } => {
-                let prim_data = &mut resources.prim_data_store[*data_handle];
+                let prim_data = &mut resources.line_decoration_data_store[*data_handle];
+                let common_data = &mut prim_data.common;
+                let line_dec_data = &mut prim_data.kind;
 
                 // Update the template this instane references, which may refresh the GPU
                 // cache with any shared template data.
-                prim_data.update(frame_state);
-
-                match &prim_data.kind {
-                    PrimitiveTemplateKind::LineDecoration { ref cache_key, .. } => {
-                        // Work out the device pixel size to be used to cache this line decoration.
-                        if is_chased {
-                            println!("\tline decoration key={:?}", cache_key);
+                line_dec_data.update(common_data, frame_state);
+
+                // Work out the device pixel size to be used to cache this line decoration.
+                if is_chased {
+                    println!("\tline decoration key={:?}", line_dec_data.cache_key);
+                }
+
+                // If we have a cache key, it's a wavy / dashed / dotted line. Otherwise, it's
+                // a simple solid line.
+                if let Some(cache_key) = line_dec_data.cache_key.as_ref() {
+                    // TODO(gw): Do we ever need / want to support scales for text decorations
+                    //           based on the current transform?
+                    let scale_factor = TypedScale::new(1.0) * frame_context.device_pixel_scale;
+                    let task_size = (LayoutSize::from_au(cache_key.size) * scale_factor).ceil().to_i32();
+                    let surfaces = &mut frame_state.surfaces;
+
+                    // Request a pre-rendered image task.
+                    // TODO(gw): This match is a bit untidy, but it should disappear completely
+                    //           once the prepare_prims and batching are unified. When that
+                    //           happens, we can use the cache handle immediately, and not need
+                    //           to temporarily store it in the primitive instance.
+                    *cache_handle = Some(frame_state.resource_cache.request_render_task(
+                        RenderTaskCacheKey {
+                            size: task_size,
+                            kind: RenderTaskCacheKeyKind::LineDecoration(cache_key.clone()),
+                        },
+                        frame_state.gpu_cache,
+                        frame_state.render_tasks,
+                        None,
+                        false,
+                        |render_tasks| {
+                            let task = RenderTask::new_line_decoration(
+                                task_size,
+                                cache_key.style,
+                                cache_key.orientation,
+                                cache_key.wavy_line_thickness.to_f32_px(),
+                                LayoutSize::from_au(cache_key.size),
+                            );
+                            let task_id = render_tasks.add(task);
+                            surfaces[pic_context.surface_index.0].tasks.push(task_id);
+                            task_id
                         }
-
-                        // If we have a cache key, it's a wavy / dashed / dotted line. Otherwise, it's
-                        // a simple solid line.
-                        if let Some(cache_key) = cache_key {
-                            // TODO(gw): Do we ever need / want to support scales for text decorations
-                            //           based on the current transform?
-                            let scale_factor = TypedScale::new(1.0) * frame_context.device_pixel_scale;
-                            let task_size = (LayoutSize::from_au(cache_key.size) * scale_factor).ceil().to_i32();
-                            let surfaces = &mut frame_state.surfaces;
-
-                            // Request a pre-rendered image task.
-                            // TODO(gw): This match is a bit untidy, but it should disappear completely
-                            //           once the prepare_prims and batching are unified. When that
-                            //           happens, we can use the cache handle immediately, and not need
-                            //           to temporarily store it in the primitive instance.
-                            *cache_handle = Some(frame_state.resource_cache.request_render_task(
-                                RenderTaskCacheKey {
-                                    size: task_size,
-                                    kind: RenderTaskCacheKeyKind::LineDecoration(cache_key.clone()),
-                                },
-                                frame_state.gpu_cache,
-                                frame_state.render_tasks,
-                                None,
-                                false,
-                                |render_tasks| {
-                                    let task = RenderTask::new_line_decoration(
-                                        task_size,
-                                        cache_key.style,
-                                        cache_key.orientation,
-                                        cache_key.wavy_line_thickness.to_f32_px(),
-                                        LayoutSize::from_au(cache_key.size),
-                                    );
-                                    let task_id = render_tasks.add(task);
-                                    surfaces[pic_context.surface_index.0].tasks.push(task_id);
-                                    task_id
-                                }
-                            ));
-                        }
-                    },
-                    _ => unreachable!(),
+                    ));
                 }
             }
             PrimitiveInstanceKind::TextRun { data_handle, run_index, .. } => {
                 let prim_data = &mut resources.text_run_data_store[*data_handle];
 
                 // Update the template this instane references, which may refresh the GPU
                 // cache with any shared template data.
                 prim_data.update(frame_state);
@@ -3526,13 +3333,13 @@ fn test_struct_sizes() {
     // The sizes of these structures are critical for performance on a number of
     // talos stress tests. If you get a failure here on CI, there's two possibilities:
     // (a) You made a structure smaller than it currently is. Great work! Update the
     //     test expectations and move on.
     // (b) You made a structure larger. This is not necessarily a problem, but should only
     //     be done with care, and after checking if talos performance regresses badly.
     assert_eq!(mem::size_of::<PrimitiveInstance>(), 120, "PrimitiveInstance size changed");
     assert_eq!(mem::size_of::<PrimitiveInstanceKind>(), 40, "PrimitiveInstanceKind size changed");
-    assert_eq!(mem::size_of::<PrimitiveTemplate>(), 96, "PrimitiveTemplate size changed");
-    assert_eq!(mem::size_of::<PrimitiveTemplateKind>(), 36, "PrimitiveTemplateKind size changed");
-    assert_eq!(mem::size_of::<PrimitiveKey>(), 116, "PrimitiveKey size changed");
-    assert_eq!(mem::size_of::<PrimitiveKeyKind>(), 88, "PrimitiveKeyKind size changed");
+    assert_eq!(mem::size_of::<PrimitiveTemplate>(), 80, "PrimitiveTemplate size changed");
+    assert_eq!(mem::size_of::<PrimitiveTemplateKind>(), 20, "PrimitiveTemplateKind size changed");
+    assert_eq!(mem::size_of::<PrimitiveKey>(), 36, "PrimitiveKey size changed");
+    assert_eq!(mem::size_of::<PrimitiveKeyKind>(), 5, "PrimitiveKeyKind size changed");
 }
new file mode 100644
--- /dev/null
+++ b/gfx/wr/webrender/src/prim_store/picture.rs
@@ -0,0 +1,239 @@
+/* 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 api::{
+    ColorU, FilterOp, LayoutRect, LayoutSize, LayoutPrimitiveInfo, MixBlendMode,
+    PropertyBinding, PropertyBindingId,
+};
+use app_units::Au;
+use display_list_flattener::{AsInstanceKind, IsVisible};
+use intern::{DataStore, Handle, Internable, Interner, InternDebug, UpdateList};
+use picture::PictureCompositeMode;
+use prim_store::{
+    PrimKey, PrimKeyCommonData, PrimTemplate, PrimTemplateCommonData,
+    PrimitiveInstanceKind, PrimitiveSceneData, PrimitiveStore, VectorKey,
+};
+
+/// Represents a hashable description of how a picture primitive
+/// will be composited into its parent.
+#[cfg_attr(feature = "capture", derive(Serialize))]
+#[cfg_attr(feature = "replay", derive(Deserialize))]
+#[derive(Debug, Clone, PartialEq, Hash, Eq)]
+pub enum PictureCompositeKey {
+    // No visual compositing effect
+    Identity,
+
+    // FilterOp
+    Blur(Au),
+    Brightness(Au),
+    Contrast(Au),
+    Grayscale(Au),
+    HueRotate(Au),
+    Invert(Au),
+    Opacity(Au),
+    OpacityBinding(PropertyBindingId, Au),
+    Saturate(Au),
+    Sepia(Au),
+    DropShadow(VectorKey, Au, ColorU),
+    ColorMatrix([Au; 20]),
+    SrgbToLinear,
+    LinearToSrgb,
+
+    // MixBlendMode
+    Multiply,
+    Screen,
+    Overlay,
+    Darken,
+    Lighten,
+    ColorDodge,
+    ColorBurn,
+    HardLight,
+    SoftLight,
+    Difference,
+    Exclusion,
+    Hue,
+    Saturation,
+    Color,
+    Luminosity,
+}
+
+impl From<Option<PictureCompositeMode>> for PictureCompositeKey {
+    fn from(mode: Option<PictureCompositeMode>) -> Self {
+        match mode {
+            Some(PictureCompositeMode::MixBlend(mode)) => {
+                match mode {
+                    MixBlendMode::Normal => PictureCompositeKey::Identity,
+                    MixBlendMode::Multiply => PictureCompositeKey::Multiply,
+                    MixBlendMode::Screen => PictureCompositeKey::Screen,
+                    MixBlendMode::Overlay => PictureCompositeKey::Overlay,
+                    MixBlendMode::Darken => PictureCompositeKey::Darken,
+                    MixBlendMode::Lighten => PictureCompositeKey::Lighten,
+                    MixBlendMode::ColorDodge => PictureCompositeKey::ColorDodge,
+                    MixBlendMode::ColorBurn => PictureCompositeKey::ColorBurn,
+                    MixBlendMode::HardLight => PictureCompositeKey::HardLight,
+                    MixBlendMode::SoftLight => PictureCompositeKey::SoftLight,
+                    MixBlendMode::Difference => PictureCompositeKey::Difference,
+                    MixBlendMode::Exclusion => PictureCompositeKey::Exclusion,
+                    MixBlendMode::Hue => PictureCompositeKey::Hue,
+                    MixBlendMode::Saturation => PictureCompositeKey::Saturation,
+                    MixBlendMode::Color => PictureCompositeKey::Color,
+                    MixBlendMode::Luminosity => PictureCompositeKey::Luminosity,
+                }
+            }
+            Some(PictureCompositeMode::Filter(op)) => {
+                match op {
+                    FilterOp::Blur(value) => PictureCompositeKey::Blur(Au::from_f32_px(value)),
+                    FilterOp::Brightness(value) => PictureCompositeKey::Brightness(Au::from_f32_px(value)),
+                    FilterOp::Contrast(value) => PictureCompositeKey::Contrast(Au::from_f32_px(value)),
+                    FilterOp::Grayscale(value) => PictureCompositeKey::Grayscale(Au::from_f32_px(value)),
+                    FilterOp::HueRotate(value) => PictureCompositeKey::HueRotate(Au::from_f32_px(value)),
+                    FilterOp::Invert(value) => PictureCompositeKey::Invert(Au::from_f32_px(value)),
+                    FilterOp::Saturate(value) => PictureCompositeKey::Saturate(Au::from_f32_px(value)),
+                    FilterOp::Sepia(value) => PictureCompositeKey::Sepia(Au::from_f32_px(value)),
+                    FilterOp::SrgbToLinear => PictureCompositeKey::SrgbToLinear,
+                    FilterOp::LinearToSrgb => PictureCompositeKey::LinearToSrgb,
+                    FilterOp::Identity => PictureCompositeKey::Identity,
+                    FilterOp::DropShadow(offset, radius, color) => {
+                        PictureCompositeKey::DropShadow(offset.into(), Au::from_f32_px(radius), color.into())
+                    }
+                    FilterOp::Opacity(binding, _) => {
+                        match binding {
+                            PropertyBinding::Value(value) => {
+                                PictureCompositeKey::Opacity(Au::from_f32_px(value))
+                            }
+                            PropertyBinding::Binding(key, default) => {
+                                PictureCompositeKey::OpacityBinding(key.id, Au::from_f32_px(default))
+                            }
+                        }
+                    }
+                    FilterOp::ColorMatrix(values) => {
+                        let mut quantized_values: [Au; 20] = [Au(0); 20];
+                        for (value, result) in values.iter().zip(quantized_values.iter_mut()) {
+                            *result = Au::from_f32_px(*value);
+                        }
+                        PictureCompositeKey::ColorMatrix(quantized_values)
+                    }
+                }
+            }
+            Some(PictureCompositeMode::Blit) |
+            Some(PictureCompositeMode::TileCache { .. }) |
+            None => {
+                PictureCompositeKey::Identity
+            }
+        }
+    }
+}
+
+#[cfg_attr(feature = "capture", derive(Serialize))]
+#[cfg_attr(feature = "replay", derive(Deserialize))]
+#[derive(Debug, Clone, Eq, PartialEq, Hash)]
+pub struct Picture {
+    pub composite_mode_key: PictureCompositeKey,
+}
+
+pub type PictureKey = PrimKey<Picture>;
+
+impl PictureKey {
+    pub fn new(
+        is_backface_visible: bool,
+        prim_size: LayoutSize,
+        prim_relative_clip_rect: LayoutRect,
+        pic: Picture,
+    ) -> Self {
+
+        PictureKey {
+            common: PrimKeyCommonData {
+                is_backface_visible,
+                prim_size: prim_size.into(),
+                prim_relative_clip_rect: prim_relative_clip_rect.into(),
+            },
+            kind: pic,
+        }
+    }
+}
+
+impl InternDebug for PictureKey {}
+
+impl AsInstanceKind<PictureDataHandle> for PictureKey {
+    /// Construct a primitive instance that matches the type
+    /// of primitive key.
+    fn as_instance_kind(
+        &self,
+        _: PictureDataHandle,
+        _: &mut PrimitiveStore,
+    ) -> PrimitiveInstanceKind {
+        // Should never be hit as this method should not be
+        // called for pictures.
+        unreachable!();
+    }
+}
+
+#[cfg_attr(feature = "capture", derive(Serialize))]
+#[cfg_attr(feature = "replay", derive(Deserialize))]
+pub struct PictureData;
+
+pub type PictureTemplate = PrimTemplate<PictureData>;
+
+impl From<PictureKey> for PictureTemplate {
+    fn from(key: PictureKey) -> Self {
+        let common = PrimTemplateCommonData::with_key_common(key.common);
+
+        PictureTemplate {
+            common,
+            kind: PictureData,
+        }
+    }
+}
+
+#[cfg_attr(feature = "capture", derive(Serialize))]
+#[cfg_attr(feature = "replay", derive(Deserialize))]
+#[derive(Clone, Copy, Debug, Hash, Eq, PartialEq)]
+pub struct PictureDataMarker;
+
+pub type PictureDataStore = DataStore<PictureKey, PictureTemplate, PictureDataMarker>;
+pub type PictureDataHandle = Handle<PictureDataMarker>;
+pub type PictureDataUpdateList = UpdateList<PictureKey>;
+pub type PictureDataInterner = Interner<PictureKey, PrimitiveSceneData, PictureDataMarker>;
+
+impl Internable for Picture {
+    type Marker = PictureDataMarker;
+    type Source = PictureKey;
+    type StoreData = PictureTemplate;
+    type InternData = PrimitiveSceneData;
+
+    /// Build a new key from self with `info`.
+    fn build_key(
+        self,
+        info: &LayoutPrimitiveInfo,
+        prim_relative_clip_rect: LayoutRect,
+    ) -> PictureKey {
+        PictureKey::new(
+            info.is_backface_visible,
+            info.rect.size,
+            prim_relative_clip_rect,
+            self
+        )
+    }
+}
+
+impl IsVisible for Picture {
+    fn is_visible(&self) -> bool {
+        true
+    }
+}
+
+#[test]
+#[cfg(target_os = "linux")]
+fn test_struct_sizes() {
+    use std::mem;
+    // The sizes of these structures are critical for performance on a number of
+    // talos stress tests. If you get a failure here on CI, there's two possibilities:
+    // (a) You made a structure smaller than it currently is. Great work! Update the
+    //     test expectations and move on.
+    // (b) You made a structure larger. This is not necessarily a problem, but should only
+    //     be done with care, and after checking if talos performance regresses badly.
+    assert_eq!(mem::size_of::<Picture>(), 84, "Picture size changed");
+    assert_eq!(mem::size_of::<PictureTemplate>(), 56, "PictureTemplate size changed");
+    assert_eq!(mem::size_of::<PictureKey>(), 112, "PictureKey size changed");
+}
--- a/gfx/wr/webrender/src/profiler.rs
+++ b/gfx/wr/webrender/src/profiler.rs
@@ -401,18 +401,20 @@ pub struct IpcProfileCounters {
     pub display_lists: ResourceProfileCounter,
 }
 
 #[derive(Clone)]
 pub struct InternProfileCounters {
     pub prims: ResourceProfileCounter,
     pub images: ResourceProfileCounter,
     pub image_borders: ResourceProfileCounter,
+    pub line_decs: ResourceProfileCounter,
     pub linear_gradients: ResourceProfileCounter,
     pub normal_borders: ResourceProfileCounter,
+    pub pictures: ResourceProfileCounter,
     pub radial_gradients: ResourceProfileCounter,
     pub text_runs: ResourceProfileCounter,
     pub yuv_images: ResourceProfileCounter,
     pub clips: ResourceProfileCounter,
 }
 
 impl IpcProfileCounters {
     pub fn set(
@@ -451,18 +453,20 @@ impl BackendProfileCounters {
                 send_time: TimeProfileCounter::new("Display List Send Time", false),
                 total_time: TimeProfileCounter::new("Total Display List Time", false),
                 display_lists: ResourceProfileCounter::new("Display Lists Sent"),
             },
             intern: InternProfileCounters {
                 prims: ResourceProfileCounter::new("Interned primitives"),
                 images: ResourceProfileCounter::new("Interned images"),
                 image_borders: ResourceProfileCounter::new("Interned image borders"),
+                line_decs: ResourceProfileCounter::new("Interned line decorations"),
                 linear_gradients: ResourceProfileCounter::new("Interned linear gradients"),
-                normal_borders: ResourceProfileCounter::new("Interner normal borders"),
+                normal_borders: ResourceProfileCounter::new("Interned normal borders"),
+                pictures: ResourceProfileCounter::new("Interned pictures"),
                 radial_gradients: ResourceProfileCounter::new("Interned radial gradients"),
                 text_runs: ResourceProfileCounter::new("Interned text runs"),
                 yuv_images: ResourceProfileCounter::new("Interned YUV images"),
                 clips: ResourceProfileCounter::new("Interned clips"),
             },
         }
     }
 
@@ -1108,18 +1112,20 @@ impl Profiler {
         );
 
         Profiler::draw_counters(
             &[
                 &backend_profile.intern.clips,
                 &backend_profile.intern.prims,
                 &backend_profile.intern.images,
                 &backend_profile.intern.image_borders,
+                &backend_profile.intern.line_decs,
                 &backend_profile.intern.linear_gradients,
                 &backend_profile.intern.normal_borders,
+                &backend_profile.intern.pictures,
                 &backend_profile.intern.radial_gradients,
                 &backend_profile.intern.text_runs,
                 &backend_profile.intern.yuv_images,
             ],
             debug_renderer,
             true,
             &mut self.draw_state
         );
--- a/gfx/wr/webrender/src/render_backend.rs
+++ b/gfx/wr/webrender/src/render_backend.rs
@@ -31,16 +31,18 @@ use gpu_cache::GpuCache;
 use hit_test::{HitTest, HitTester};
 use internal_types::{DebugOutput, FastHashMap, FastHashSet, RenderedDocument, ResultMsg};
 use picture::RetainedTiles;
 use prim_store::{PrimitiveDataStore, PrimitiveScratchBuffer, PrimitiveInstance};
 use prim_store::{PrimitiveInstanceKind, PrimTemplateCommonData};
 use prim_store::borders::{ImageBorderDataStore, NormalBorderDataStore};
 use prim_store::gradient::{LinearGradientDataStore, RadialGradientDataStore};
 use prim_store::image::{ImageDataStore, YuvImageDataStore};
+use prim_store::line_dec::LineDecorationDataStore;
+use prim_store::picture::PictureDataStore;
 use prim_store::text_run::TextRunDataStore;
 use profiler::{BackendProfileCounters, IpcProfileCounters, ResourceProfileCounters};
 use record::ApiRecordingReceiver;
 use renderer::{AsyncPropertySampler, PipelineInfo};
 use resource_cache::ResourceCache;
 #[cfg(feature = "replay")]
 use resource_cache::PlainCacheOwn;
 #[cfg(any(feature = "capture", feature = "replay"))]
@@ -204,53 +206,61 @@ pub struct FrameResources {
     /// in sync with the clip interner in the scene builder for each document.
     pub clip_data_store: ClipDataStore,
 
     /// Currently active / available primitives. Kept in sync with the
     /// primitive interner in the scene builder, per document.
     pub prim_data_store: PrimitiveDataStore,
     pub image_data_store: ImageDataStore,
     pub image_border_data_store: ImageBorderDataStore,
+    pub line_decoration_data_store: LineDecorationDataStore,
     pub linear_grad_data_store: LinearGradientDataStore,
     pub normal_border_data_store: NormalBorderDataStore,
+    pub picture_data_store: PictureDataStore,
     pub radial_grad_data_store: RadialGradientDataStore,
     pub text_run_data_store: TextRunDataStore,
     pub yuv_image_data_store: YuvImageDataStore,
 }
 
 impl FrameResources {
     pub fn as_common_data(
         &self,
         prim_inst: &PrimitiveInstance
     ) -> &PrimTemplateCommonData {
         match prim_inst.kind {
-            PrimitiveInstanceKind::Picture { data_handle, .. } |
-            PrimitiveInstanceKind::LineDecoration { data_handle, .. } |
             PrimitiveInstanceKind::Rectangle { data_handle, .. } |
             PrimitiveInstanceKind::Clear { data_handle, .. } => {
                 let prim_data = &self.prim_data_store[data_handle];
                 &prim_data.common
             }
             PrimitiveInstanceKind::Image { data_handle, .. } => {
                 let prim_data = &self.image_data_store[data_handle];
                 &prim_data.common
             }
             PrimitiveInstanceKind::ImageBorder { data_handle, .. } => {
                 let prim_data = &self.image_border_data_store[data_handle];
                 &prim_data.common
             }
+            PrimitiveInstanceKind::LineDecoration { data_handle, .. } => {
+                let prim_data = &self.line_decoration_data_store[data_handle];
+                &prim_data.common
+            }
             PrimitiveInstanceKind::LinearGradient { data_handle, .. } => {
                 let prim_data = &self.linear_grad_data_store[data_handle];
                 &prim_data.common
             }
             PrimitiveInstanceKind::NormalBorder { data_handle, .. } => {
                 let prim_data = &self.normal_border_data_store[data_handle];
                 &prim_data.common
             }
-            PrimitiveInstanceKind::RadialGradient { data_handle, .. } =>{
+            PrimitiveInstanceKind::Picture { data_handle, .. } => {
+                let prim_data = &self.picture_data_store[data_handle];
+                &prim_data.common
+            }
+            PrimitiveInstanceKind::RadialGradient { data_handle, .. } => {
                 let prim_data = &self.radial_grad_data_store[data_handle];
                 &prim_data.common
             }
             PrimitiveInstanceKind::TextRun { data_handle, .. }  => {
                 let prim_data = &self.text_run_data_store[data_handle];
                 &prim_data.common
             }
             PrimitiveInstanceKind::YuvImage { data_handle, .. } => {
@@ -1242,24 +1252,32 @@ impl RenderBackend {
             doc.resources.image_data_store.apply_updates(
                 updates.image_updates,
                 &mut profile_counters.intern.images,
             );
             doc.resources.image_border_data_store.apply_updates(
                 updates.image_border_updates,
                 &mut profile_counters.intern.image_borders,
             );
+            doc.resources.line_decoration_data_store.apply_updates(
+                updates.line_decoration_updates,
+                &mut profile_counters.intern.line_decs,
+            );
             doc.resources.linear_grad_data_store.apply_updates(
                 updates.linear_grad_updates,
                 &mut profile_counters.intern.linear_gradients,
             );
             doc.resources.normal_border_data_store.apply_updates(
                 updates.normal_border_updates,
                 &mut profile_counters.intern.normal_borders,
             );
+            doc.resources.picture_data_store.apply_updates(
+                updates.picture_updates,
+                &mut profile_counters.intern.pictures,
+            );
             doc.resources.radial_grad_data_store.apply_updates(
                 updates.radial_grad_updates,
                 &mut profile_counters.intern.radial_gradients,
             );
             doc.resources.text_run_data_store.apply_updates(
                 updates.text_run_updates,
                 &mut profile_counters.intern.text_runs,
             );
--- a/gfx/wr/webrender/src/render_task.rs
+++ b/gfx/wr/webrender/src/render_task.rs
@@ -16,18 +16,19 @@ use device::TextureFilter;
 use euclid::{TypedPoint2D, TypedVector2D};
 use freelist::{FreeList, FreeListHandle, WeakFreeListHandle};
 use glyph_rasterizer::GpuGlyphCacheKey;
 use gpu_cache::{GpuCache, GpuCacheAddress, GpuCacheHandle};
 use gpu_types::{BorderInstance, ImageSource, UvRectKind};
 use internal_types::{CacheTextureId, FastHashMap, LayerIndex, SavedTargetIndex};
 #[cfg(feature = "pathfinder")]
 use pathfinder_partitioner::mesh::Mesh;
-use prim_store::{PictureIndex, LineDecorationCacheKey};
+use prim_store::PictureIndex;
 use prim_store::image::ImageCacheKey;
+use prim_store::line_dec::LineDecorationCacheKey;
 #[cfg(feature = "debugger")]
 use print_tree::{PrintTreePrinter};
 use render_backend::FrameId;
 use resource_cache::{CacheItem, ResourceCache};
 use surface::SurfaceCacheKey;
 use std::{cmp, ops, mem, usize, f32, i32, u32};
 use texture_cache::{TextureCache, TextureCacheHandle, Eviction};
 use tiling::{RenderPass, RenderTargetIndex};
--- a/gfx/wr/webrender/src/scene_builder.rs
+++ b/gfx/wr/webrender/src/scene_builder.rs
@@ -23,16 +23,20 @@ use prim_store::borders::{
 use prim_store::gradient::{
     LinearGradient, LinearGradientDataInterner, LinearGradientDataUpdateList,
     RadialGradient, RadialGradientDataInterner, RadialGradientDataUpdateList
 };
 use prim_store::image::{
     Image, ImageDataInterner, ImageDataUpdateList,
     YuvImage, YuvImageDataInterner, YuvImageDataUpdateList,
 };
+use prim_store::line_dec::{
+    LineDecoration, LineDecorationDataInterner, LineDecorationDataUpdateList
+};
+use prim_store::picture::{PictureDataInterner, Picture, PictureDataUpdateList};
 use prim_store::text_run::{TextRunDataInterner, TextRun, TextRunDataUpdateList};
 use resource_cache::{BlobImageRasterizerEpoch, FontInstanceMap};
 use render_backend::DocumentView;
 use renderer::{PipelineInfo, SceneBuilderHooks};
 use scene::Scene;
 use std::sync::mpsc::{channel, Receiver, Sender};
 use std::mem::replace;
 use time::precise_time_ns;
@@ -40,18 +44,20 @@ use util::drain_filter;
 use std::thread;
 use std::time::Duration;
 
 pub struct DocumentResourceUpdates {
     pub clip_updates: ClipDataUpdateList,
     pub prim_updates: PrimitiveDataUpdateList,
     pub image_updates: ImageDataUpdateList,
     pub image_border_updates: ImageBorderDataUpdateList,
+    pub line_decoration_updates: LineDecorationDataUpdateList,
     pub linear_grad_updates: LinearGradientDataUpdateList,
     pub normal_border_updates: NormalBorderDataUpdateList,
+    pub picture_updates: PictureDataUpdateList,
     pub radial_grad_updates: RadialGradientDataUpdateList,
     pub text_run_updates: TextRunDataUpdateList,
     pub yuv_image_updates: YuvImageDataUpdateList,
 }
 
 /// Represents the work associated to a transaction before scene building.
 pub struct Transaction {
     pub document_id: DocumentId,
@@ -194,18 +200,20 @@ pub enum SceneSwapResult {
 #[cfg_attr(feature = "capture", derive(Serialize))]
 #[cfg_attr(feature = "replay", derive(Deserialize))]
 #[derive(Default)]
 pub struct DocumentResources {
     pub clip_interner: ClipDataInterner,
     pub prim_interner: PrimitiveDataInterner,
     pub image_interner: ImageDataInterner,
     pub image_border_interner: ImageBorderDataInterner,
+    pub line_decoration_interner: LineDecorationDataInterner,
     pub linear_grad_interner: LinearGradientDataInterner,
     pub normal_border_interner: NormalBorderDataInterner,
+    pub picture_interner: PictureDataInterner,
     pub radial_grad_interner: RadialGradientDataInterner,
     pub text_run_interner: TextRunDataInterner,
     pub yuv_image_interner: YuvImageDataInterner,
 }
 
 // Access to `DocumentResources` interners by `Internable`
 pub trait InternerMut<I: Internable>
 {
@@ -224,18 +232,20 @@ macro_rules! impl_internet_mut {
             }
         })*
     }
 }
 
 impl_internet_mut! {
     Image: image_interner,
     ImageBorder: image_border_interner,
+    LineDecoration: line_decoration_interner,
     LinearGradient: linear_grad_interner,
     NormalBorderPrim: normal_border_interner,
+    Picture: picture_interner,
     PrimitiveKeyKind: prim_interner,
     RadialGradient: radial_grad_interner,
     TextRun: text_run_interner,
     YuvImage: yuv_image_interner,
 }
 
 // A document in the scene builder contains the current scene,
 // as well as a persistent clip interner. This allows clips
@@ -412,26 +422,36 @@ impl SceneBuilder {
                     .image_interner
                     .end_frame_and_get_pending_updates();
 
                 let image_border_updates = item
                     .doc_resources
                     .image_border_interner
                     .end_frame_and_get_pending_updates();
 
+                let line_decoration_updates = item
+                    .doc_resources
+                    .line_decoration_interner
+                    .end_frame_and_get_pending_updates();
+
                 let linear_grad_updates = item
                     .doc_resources
                     .linear_grad_interner
                     .end_frame_and_get_pending_updates();
 
                 let normal_border_updates = item
                     .doc_resources
                     .normal_border_interner
                     .end_frame_and_get_pending_updates();
 
+                let picture_updates = item
+                    .doc_resources
+                    .picture_interner
+                    .end_frame_and_get_pending_updates();
+
                 let radial_grad_updates = item
                     .doc_resources
                     .radial_grad_interner
                     .end_frame_and_get_pending_updates();
 
                 let text_run_updates = item
                     .doc_resources
                     .text_run_interner
@@ -443,18 +463,20 @@ impl SceneBuilder {
                     .end_frame_and_get_pending_updates();
 
                 doc_resource_updates = Some(
                     DocumentResourceUpdates {
                         clip_updates,
                         prim_updates,
                         image_updates,
                         image_border_updates,
+                        line_decoration_updates,
                         linear_grad_updates,
                         normal_border_updates,
+                        picture_updates,
                         radial_grad_updates,
                         text_run_updates,
                         yuv_image_updates,
                     }
                 );
 
                 built_scene = Some(BuiltScene {
                     scene: new_scene,
@@ -566,26 +588,36 @@ impl SceneBuilder {
                     .image_interner
                     .end_frame_and_get_pending_updates();
 
                 let image_border_updates = doc
                     .resources
                     .image_border_interner
                     .end_frame_and_get_pending_updates();
 
+                let line_decoration_updates = doc
+                    .resources
+                    .line_decoration_interner
+                    .end_frame_and_get_pending_updates();
+
                 let linear_grad_updates = doc
                     .resources
                     .linear_grad_interner
                     .end_frame_and_get_pending_updates();
 
                 let normal_border_updates = doc
                     .resources
                     .normal_border_interner
                     .end_frame_and_get_pending_updates();
 
+                let picture_updates = doc
+                    .resources
+                    .picture_interner
+                    .end_frame_and_get_pending_updates();
+
                 let radial_grad_updates = doc
                     .resources
                     .radial_grad_interner
                     .end_frame_and_get_pending_updates();
 
                 let text_run_updates = doc
                     .resources
                     .text_run_interner
@@ -597,18 +629,20 @@ impl SceneBuilder {
                     .end_frame_and_get_pending_updates();
 
                 doc_resource_updates = Some(
                     DocumentResourceUpdates {
                         clip_updates,
                         prim_updates,
                         image_updates,
                         image_border_updates,
+                        line_decoration_updates,
                         linear_grad_updates,
                         normal_border_updates,
+                        picture_updates,
                         radial_grad_updates,
                         text_run_updates,
                         yuv_image_updates,
                     }
                 );
 
                 built_scene = Some(BuiltScene {
                     scene: new_scene,