Bug 1510086 - Update webrender to commit f3e489eebe9ffd5229c93aa4e17f4c3a7e6cb31d (WR PR #3349). r=kats
authorWR Updater Bot <graphics-team@mozilla.staktrace.com>
Tue, 27 Nov 2018 02:55:17 +0000
changeset 507413 bf904b3d9bed20abc2c289e60f81699a79dbaff1
parent 507412 093ef9c7cb94cd97d45bc3b453f62128114dbb7a
child 507414 e018665be14b6bdb3542ca4b3e0c47a5c7f4cdcb
push id1905
push userffxbld-merge
push dateMon, 21 Jan 2019 12:33:13 +0000
treeherdermozilla-release@c2fca1944d8c [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewerskats
bugs1510086
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 1510086 - Update webrender to commit f3e489eebe9ffd5229c93aa4e17f4c3a7e6cb31d (WR PR #3349). r=kats https://github.com/servo/webrender/pull/3349 Differential Revision: https://phabricator.services.mozilla.com/D13030
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.rs
gfx/wr/webrender/src/surface.rs
--- a/gfx/webrender_bindings/revision.txt
+++ b/gfx/webrender_bindings/revision.txt
@@ -1,1 +1,1 @@
-05bdcae134d73aca7bb48358e91de1f8aef27773
+f3e489eebe9ffd5229c93aa4e17f4c3a7e6cb31d
--- a/gfx/wr/webrender/src/batch.rs
+++ b/gfx/wr/webrender/src/batch.rs
@@ -10,20 +10,20 @@ use clip_scroll_tree::{ClipScrollTree, R
 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, PrimitiveTemplateKind};
+use prim_store::{BrushKind, BrushPrimitive, DeferredResolve, PrimitiveTemplateKind, PrimitiveDataStore};
 use prim_store::{EdgeAaSegmentMask, ImageSource, PrimitiveInstanceKind, PrimitiveStore};
 use prim_store::{VisibleGradientTile, PrimitiveInstance, PrimitiveOpacity, SegmentInstanceIndex};
-use prim_store::{BrushSegment, ClipMaskKind, ClipTaskIndex, PrimitiveDetails};
+use prim_store::{BrushSegment, ClipMaskKind, ClipTaskIndex, PrimitiveDetails, Primitive};
 use render_task::{RenderTaskAddress, RenderTaskId, RenderTaskTree};
 use renderer::{BlendMode, ImageBufferKind, ShaderColorMode};
 use renderer::BLOCKS_PER_UV_RECT;
 use resource_cache::{CacheItem, GlyphFetchResult, ImageRequest, ResourceCache, ImageProperties};
 use scene::FilterOpHelpers;
 use smallvec::SmallVec;
 use std::{f32, i32, usize};
 use tiling::{RenderTargetContext};
@@ -901,16 +901,17 @@ impl AlphaBatchBuilder {
                             let pic_index = match prim_instance.kind {
                                 PrimitiveInstanceKind::Picture { pic_index } => pic_index,
                                 PrimitiveInstanceKind::LineDecoration { .. } |
                                 PrimitiveInstanceKind::TextRun { .. } |
                                 PrimitiveInstanceKind::LegacyPrimitive { .. } |
                                 PrimitiveInstanceKind::NormalBorder { .. } |
                                 PrimitiveInstanceKind::ImageBorder { .. } |
                                 PrimitiveInstanceKind::Rectangle { .. } |
+                                PrimitiveInstanceKind::YuvImage { .. } |
                                 PrimitiveInstanceKind::Clear => {
                                     unreachable!();
                                 }
                             };
                             let pic = &ctx.prim_store.pictures[pic_index.0];
 
                             // Get clip task, if set, for the picture primitive.
                             let clip_task_address = get_clip_task_address(
@@ -1406,17 +1407,16 @@ impl AlphaBatchBuilder {
                 // If the primitive is internally decomposed into multiple sub-primitives we may not
                 // use some of the per-primitive data and get it from each sub-primitive instead.
                 let is_multiple_primitives = match prim.details {
                     PrimitiveDetails::Brush(ref brush) => {
                         match brush.kind {
                             BrushKind::Image { ref visible_tiles, .. } => !visible_tiles.is_empty(),
                             BrushKind::LinearGradient { ref visible_tiles, .. } => !visible_tiles.is_empty(),
                             BrushKind::RadialGradient { ref visible_tiles, .. } => !visible_tiles.is_empty(),
-                            _ => false,
                         }
                     }
                 };
 
                 let specified_blend_mode = prim_instance.get_blend_mode(&prim.details);
 
                 match prim.details {
                     PrimitiveDetails::Brush(ref brush) => {
@@ -1678,16 +1678,126 @@ impl AlphaBatchBuilder {
                     bounding_rect,
                     transform_kind,
                     render_tasks,
                     z_id,
                     prim_instance.clip_task_index,
                     ctx,
                 );
             }
+            (
+                PrimitiveInstanceKind::YuvImage { segment_instance_index, .. },
+                PrimitiveTemplateKind::YuvImage { format, yuv_key, image_rendering, color_depth, color_space, .. }
+            ) => {
+                let mut textures = BatchTextures::no_texture();
+                let mut uv_rect_addresses = [0; 3];
+
+                //yuv channel
+                let channel_count = format.get_plane_num();
+                debug_assert!(channel_count <= 3);
+                for channel in 0 .. channel_count {
+                    let image_key = yuv_key[channel];
+
+                    let cache_item = resolve_image(
+                        ImageRequest {
+                            key: image_key,
+                            rendering: *image_rendering,
+                            tile: None,
+                        },
+                        ctx.resource_cache,
+                        gpu_cache,
+                        deferred_resolves,
+                    );
+
+                    if cache_item.texture_id == TextureSource::Invalid {
+                        warn!("Warnings: skip a PrimitiveKind::YuvImage");
+                        return;
+                    }
+
+                    textures.colors[channel] = cache_item.texture_id;
+                    uv_rect_addresses[channel] = cache_item.uv_rect_handle.as_int(gpu_cache);
+                }
+
+                // All yuv textures should be the same type.
+                let buffer_kind = get_buffer_kind(textures.colors[0]);
+                assert!(
+                    textures.colors[1 .. format.get_plane_num()]
+                        .iter()
+                        .all(|&tid| buffer_kind == get_buffer_kind(tid))
+                );
+
+                let kind = BrushBatchKind::YuvImage(
+                    buffer_kind,
+                    *format,
+                    *color_depth,
+                    *color_space,
+                );
+
+                let batch_params = BrushBatchParameters::shared(
+                    kind,
+                    textures,
+                    [
+                        uv_rect_addresses[0],
+                        uv_rect_addresses[1],
+                        uv_rect_addresses[2],
+                    ],
+                    0,
+                );
+
+                let specified_blend_mode = BlendMode::PremultipliedAlpha;
+
+                let non_segmented_blend_mode = if !prim_data.opacity.is_opaque ||
+                    prim_instance.clip_task_index != ClipTaskIndex::INVALID ||
+                    transform_kind == TransformedRectKind::Complex
+                {
+                    specified_blend_mode
+                } else {
+                    BlendMode::None
+                };
+
+                debug_assert!(*segment_instance_index != SegmentInstanceIndex::INVALID);
+                let (prim_cache_address, segments) = if *segment_instance_index == SegmentInstanceIndex::UNUSED {
+                    (gpu_cache.get_address(&prim_data.gpu_cache_handle), None)
+                } else {
+                    let segment_instance = &ctx.scratch.segment_instances[*segment_instance_index];
+                    let segments = Some(&ctx.scratch.segments[segment_instance.segments_range]);
+                    (gpu_cache.get_address(&segment_instance.gpu_cache_handle), segments)
+                };
+
+                let prim_header = PrimitiveHeader {
+                    local_rect: prim_data.prim_rect,
+                    local_clip_rect: prim_instance.combined_local_clip_rect,
+                    task_address,
+                    specific_prim_address: prim_cache_address,
+                    clip_task_address,
+                    transform_id,
+                };
+
+                let prim_header_index = prim_headers.push(
+                    &prim_header,
+                    z_id,
+                    batch_params.prim_user_data,
+                );
+
+                self.add_segmented_prim_to_batch(
+                    segments,
+                    prim_data.opacity,
+                    &batch_params,
+                    specified_blend_mode,
+                    non_segmented_blend_mode,
+                    prim_header_index,
+                    clip_task_address,
+                    bounding_rect,
+                    transform_kind,
+                    render_tasks,
+                    z_id,
+                    prim_instance.clip_task_index,
+                    ctx,
+                );
+            }
             _ => {
                 unreachable!();
             }
         }
     }
 
     fn add_image_tile_to_batch(
         &mut self,
@@ -2084,72 +2194,16 @@ impl BrushPrimitive {
                     [
                         stops_handle.as_int(gpu_cache),
                         0,
                         0,
                     ],
                     0,
                 ))
             }
-            BrushKind::YuvImage { format, yuv_key, image_rendering, color_depth, color_space } => {
-                let mut textures = BatchTextures::no_texture();
-                let mut uv_rect_addresses = [0; 3];
-
-                //yuv channel
-                let channel_count = format.get_plane_num();
-                debug_assert!(channel_count <= 3);
-                for channel in 0 .. channel_count {
-                    let image_key = yuv_key[channel];
-
-                    let cache_item = resolve_image(
-                        ImageRequest {
-                            key: image_key,
-                            rendering: image_rendering,
-                            tile: None,
-                        },
-                        resource_cache,
-                        gpu_cache,
-                        deferred_resolves,
-                    );
-
-                    if cache_item.texture_id == TextureSource::Invalid {
-                        warn!("Warnings: skip a PrimitiveKind::YuvImage");
-                        return None;
-                    }
-
-                    textures.colors[channel] = cache_item.texture_id;
-                    uv_rect_addresses[channel] = cache_item.uv_rect_handle.as_int(gpu_cache);
-                }
-
-                // All yuv textures should be the same type.
-                let buffer_kind = get_buffer_kind(textures.colors[0]);
-                assert!(
-                    textures.colors[1 .. format.get_plane_num()]
-                        .iter()
-                        .all(|&tid| buffer_kind == get_buffer_kind(tid))
-                );
-
-                let kind = BrushBatchKind::YuvImage(
-                    buffer_kind,
-                    format,
-                    color_depth,
-                    color_space,
-                );
-
-                Some(BrushBatchParameters::shared(
-                    kind,
-                    textures,
-                    [
-                        uv_rect_addresses[0],
-                        uv_rect_addresses[1],
-                        uv_rect_addresses[2],
-                    ],
-                    0,
-                ))
-            }
         }
     }
 }
 
 impl PrimitiveInstance {
     fn get_blend_mode(
         &self,
         details: &PrimitiveDetails,
@@ -2158,40 +2212,60 @@ impl PrimitiveInstance {
             PrimitiveDetails::Brush(ref brush) => {
                 match brush.kind {
                     BrushKind::Image { alpha_type, .. } => {
                         match alpha_type {
                             AlphaType::PremultipliedAlpha => BlendMode::PremultipliedAlpha,
                             AlphaType::Alpha => BlendMode::Alpha,
                         }
                     }
-                    BrushKind::YuvImage { .. } |
                     BrushKind::RadialGradient { .. } |
                     BrushKind::LinearGradient { .. } => {
                         BlendMode::PremultipliedAlpha
                     }
                 }
             }
         }
     }
 
     pub fn is_cacheable(
         &self,
-        details: &PrimitiveDetails,
-        resource_cache: &ResourceCache
+        primitives: &[Primitive],
+        prim_data_store: &PrimitiveDataStore,
+        resource_cache: &ResourceCache,
     ) -> bool {
-        let image_key = match *details {
-            PrimitiveDetails::Brush(BrushPrimitive { kind: BrushKind::Image{ request, ..  }, .. }) => {
-                request.key
+        let image_key = match self.kind {
+            PrimitiveInstanceKind::LegacyPrimitive { prim_index, .. } => {
+                let prim = &primitives[prim_index.0];
+                match prim.details {
+                    PrimitiveDetails::Brush(BrushPrimitive { kind: BrushKind::Image{ request, ..  }, .. }) => {
+                        request.key
+                    }
+                    PrimitiveDetails::Brush(_) => {
+                        return true
+                    }
+                }
             }
-            PrimitiveDetails::Brush(BrushPrimitive { kind: BrushKind::YuvImage{ yuv_key, .. }, .. }) => {
-                yuv_key[0]
+            PrimitiveInstanceKind::YuvImage { .. } => {
+                let prim_data = &prim_data_store[self.prim_data_handle];
+                match prim_data.kind {
+                    PrimitiveTemplateKind::YuvImage { ref yuv_key, .. } => {
+                        yuv_key[0]
+                    }
+                    _ => unreachable!(),
+                }
             }
-            PrimitiveDetails::Brush(_) => {
-                return true
+            PrimitiveInstanceKind::Picture { .. } |
+            PrimitiveInstanceKind::TextRun { .. } |
+            PrimitiveInstanceKind::LineDecoration { .. } |
+            PrimitiveInstanceKind::NormalBorder { .. } |
+            PrimitiveInstanceKind::ImageBorder { .. } |
+            PrimitiveInstanceKind::Rectangle { .. } |
+            PrimitiveInstanceKind::Clear => {
+                return true;
             }
         };
         match resource_cache.get_image_properties(image_key) {
             Some(ImageProperties { external_image: Some(_), .. }) => {
                 false
             }
             _ => true
         }
--- a/gfx/wr/webrender/src/display_list_flattener.rs
+++ b/gfx/wr/webrender/src/display_list_flattener.rs
@@ -2088,32 +2088,27 @@ impl<'a> DisplayListFlattener<'a> {
     ) {
         let format = yuv_data.get_format();
         let yuv_key = match yuv_data {
             YuvData::NV12(plane_0, plane_1) => [plane_0, plane_1, ImageKey::DUMMY],
             YuvData::PlanarYCbCr(plane_0, plane_1, plane_2) => [plane_0, plane_1, plane_2],
             YuvData::InterleavedYCbCr(plane_0) => [plane_0, ImageKey::DUMMY, ImageKey::DUMMY],
         };
 
-        let prim = BrushPrimitive::new(
-            BrushKind::YuvImage {
-                yuv_key,
-                format,
-                color_depth,
-                color_space,
-                image_rendering,
-            },
-            None,
-        );
-
         self.add_primitive(
             clip_and_scroll,
             info,
             Vec::new(),
-            PrimitiveContainer::Brush(prim),
+            PrimitiveContainer::YuvImage {
+                color_depth,
+                yuv_key,
+                format,
+                color_space,
+                image_rendering,
+            },
         );
     }
 
     pub fn map_clip_and_scroll(&mut self, info: &ClipAndScrollInfo) -> ScrollNodeAndClipChain {
         ScrollNodeAndClipChain::new(
             self.id_to_index_mapper.get_spatial_node_index(info.scroll_node_id),
             self.id_to_index_mapper.get_clip_chain_id(&info.clip_node_id())
         )
--- a/gfx/wr/webrender/src/picture.rs
+++ b/gfx/wr/webrender/src/picture.rs
@@ -15,17 +15,17 @@ use euclid::approxeq::ApproxEq;
 use internal_types::{FastHashMap, PlaneSplitter};
 use frame_builder::{FrameBuildingContext, FrameBuildingState, PictureState, PictureContext};
 use gpu_cache::{GpuCacheAddress, GpuCacheHandle};
 use gpu_types::{TransformPalette, TransformPaletteId, UvRectKind};
 use internal_types::FastHashSet;
 use plane_split::{Clipper, Polygon, Splitter};
 use prim_store::{PictureIndex, PrimitiveInstance, SpaceMapper, VisibleFace, PrimitiveInstanceKind};
 use prim_store::{get_raster_rects, PrimitiveDataInterner, PrimitiveDataStore, CoordinateSpaceMapping};
-use prim_store::{PrimitiveDetails, BrushKind, Primitive, OpacityBindingStorage};
+use prim_store::{PrimitiveDetails, BrushKind, Primitive, OpacityBindingStorage, PrimitiveTemplateKind};
 use render_task::{ClearMode, RenderTask, RenderTaskCacheEntryHandle, TileBlit};
 use render_task::{RenderTaskCacheKey, RenderTaskCacheKeyKind, RenderTaskId, RenderTaskLocation};
 use resource_cache::ResourceCache;
 use scene::{FilterOpHelpers, SceneProperties};
 use smallvec::SmallVec;
 use surface::{SurfaceDescriptor, TransformKey};
 use std::{mem, ops};
 use texture_cache::{Eviction, TextureCacheHandle};
@@ -377,41 +377,41 @@ impl TileCache {
         let y0 = (rect.origin.y / self.local_tile_size.height).floor() as i32;
         let x1 = ((rect.origin.x + rect.size.width) / self.local_tile_size.width).ceil() as i32;
         let y1 = ((rect.origin.y + rect.size.height) / self.local_tile_size.height).ceil() as i32;
 
         // Update the tile array allocation if needed.
         self.reconfigure_tiles_if_required(x0, y0, x1, y1);
 
         // Build the list of resources that this primitive has dependencies on.
-        let mut is_cacheable = true;
         let mut opacity_bindings: SmallVec<[(PropertyBindingId, f32); 4]> = SmallVec::new();
         let mut clip_chain_spatial_nodes: SmallVec<[SpatialNodeIndex; 8]> = SmallVec::new();
         let mut image_keys: SmallVec<[ImageKey; 8]> = SmallVec::new();
         let mut current_clip_chain_id = prim_instance.clip_chain_id;
 
+        // Some primitives can not be cached (e.g. external video images)
+        let is_cacheable = prim_instance.is_cacheable(
+            primitives,
+            prim_data_store,
+            resource_cache,
+        );
+
         match prim_instance.kind {
             PrimitiveInstanceKind::Picture { pic_index } => {
                 // Pictures can depend on animated opacity bindings.
                 let pic = &pictures[pic_index.0];
                 if let Some(PictureCompositeMode::Filter(FilterOp::Opacity(binding, _))) = pic.requested_composite_mode {
                     if let PropertyBinding::Binding(key, default) = binding {
                         opacity_bindings.push((key.id, default));
                     }
                 }
             }
             PrimitiveInstanceKind::LegacyPrimitive { prim_index } => {
                 let prim = &primitives[prim_index.0];
 
-                // Some primitives can not be cached (e.g. external video images)
-                is_cacheable = prim_instance.is_cacheable(
-                    &prim.details,
-                    resource_cache,
-                );
-
                 match prim.details {
                     PrimitiveDetails::Brush(ref brush) => {
                         match brush.kind {
                             // Rectangles and images may depend on opacity bindings.
                             // TODO(gw): In future, we might be able to completely remove
                             //           opacity collapsing support. It's of limited use
                             //           once we have full picture caching.
                             BrushKind::Image { opacity_binding_index, ref request, .. } => {
@@ -419,34 +419,41 @@ impl TileCache {
                                 for binding in &opacity_binding.bindings {
                                     if let PropertyBinding::Binding(key, default) = binding {
                                         opacity_bindings.push((key.id, *default));
                                     }
                                 }
 
                                 image_keys.push(request.key);
                             }
-                            BrushKind::YuvImage { ref yuv_key, .. } => {
-                                image_keys.extend_from_slice(yuv_key);
-                            }
                             BrushKind::RadialGradient { .. } |
                             BrushKind::LinearGradient { .. } => {
                             }
                         }
                     }
                 }
             }
             PrimitiveInstanceKind::Rectangle { opacity_binding_index, .. } => {
                 let opacity_binding = &opacity_binding_store[opacity_binding_index];
                 for binding in &opacity_binding.bindings {
                     if let PropertyBinding::Binding(key, default) = binding {
                         opacity_bindings.push((key.id, *default));
                     }
                 }
             }
+            PrimitiveInstanceKind::YuvImage { .. } => {
+                match prim_data.kind {
+                    PrimitiveTemplateKind::YuvImage { ref yuv_key, .. } => {
+                        image_keys.extend_from_slice(yuv_key);
+                    }
+                    _ => {
+                        unreachable!();
+                    }
+                }
+            }
             PrimitiveInstanceKind::TextRun { .. } |
             PrimitiveInstanceKind::LineDecoration { .. } |
             PrimitiveInstanceKind::Clear |
             PrimitiveInstanceKind::NormalBorder { .. } |
             PrimitiveInstanceKind::ImageBorder { .. } => {
                 // These don't contribute dependencies
             }
         }
--- a/gfx/wr/webrender/src/prim_store.rs
+++ b/gfx/wr/webrender/src/prim_store.rs
@@ -381,17 +381,24 @@ pub enum PrimitiveKeyKind {
         slice: SideOffsets2D<i32>,
         fill: bool,
         repeat_horizontal: RepeatMode,
         repeat_vertical: RepeatMode,
         outset: SideOffsets2D<Au>,
     },
     Rectangle {
         color: ColorU,
-    }
+    },
+    YuvImage {
+        color_depth: ColorDepth,
+        yuv_key: [ImageKey; 3],
+        format: YuvFormat,
+        color_space: YuvColorSpace,
+        image_rendering: ImageRendering,
+    },
 }
 
 #[cfg_attr(feature = "capture", derive(Serialize))]
 #[cfg_attr(feature = "replay", derive(Deserialize))]
 #[derive(Debug, Clone, PartialEq)]
 pub struct RectangleKey {
     x: f32,
     y: f32,
@@ -491,16 +498,21 @@ impl PrimitiveKey {
                 }
             }
             PrimitiveKeyKind::Rectangle { .. } => {
                 PrimitiveInstanceKind::Rectangle {
                     opacity_binding_index: OpacityBindingIndex::INVALID,
                     segment_instance_index: SegmentInstanceIndex::INVALID,
                 }
             }
+            PrimitiveKeyKind::YuvImage { .. } => {
+                PrimitiveInstanceKind::YuvImage {
+                    segment_instance_index: SegmentInstanceIndex::INVALID,
+                }
+            }
             PrimitiveKeyKind::Unused => {
                 // Should never be hit as this method should not be
                 // called for old style primitives.
                 unreachable!();
             }
         }
     }
 }
@@ -533,16 +545,23 @@ pub enum PrimitiveTemplateKind {
     },
     ImageBorder {
         request: ImageRequest,
         brush_segments: Vec<BrushSegment>,
     },
     Rectangle {
         color: ColorF,
     },
+    YuvImage {
+        color_depth: ColorDepth,
+        yuv_key: [ImageKey; 3],
+        format: YuvFormat,
+        color_space: YuvColorSpace,
+        image_rendering: ImageRendering,
+    },
     Clear,
     Unused,
 }
 
 /// Construct the primitive template data from a primitive key. This
 /// is invoked when a primitive key is created and the interner
 /// doesn't currently contain a primitive with this key.
 impl PrimitiveKeyKind {
@@ -625,16 +644,25 @@ impl PrimitiveKeyKind {
                     brush_segments,
                 }
             }
             PrimitiveKeyKind::Rectangle { color, .. } => {
                 PrimitiveTemplateKind::Rectangle {
                     color: color.into(),
                 }
             }
+            PrimitiveKeyKind::YuvImage { color_depth, yuv_key, format, color_space, image_rendering, .. } => {
+                PrimitiveTemplateKind::YuvImage {
+                    color_depth,
+                    yuv_key,
+                    format,
+                    color_space,
+                    image_rendering,
+                }
+            }
             PrimitiveKeyKind::LineDecoration { cache_key, color } => {
                 PrimitiveTemplateKind::LineDecoration {
                     cache_key,
                     color: color.into(),
                 }
             }
         }
     }
@@ -667,95 +695,190 @@ impl From<PrimitiveKey> for PrimitiveTem
             clip_rect,
             kind,
             gpu_cache_handle: GpuCacheHandle::new(),
             opacity: PrimitiveOpacity::translucent(),
         }
     }
 }
 
+impl PrimitiveTemplateKind {
+    /// Write any GPU blocks for the primitive template to the given request object.
+    fn write_prim_gpu_blocks(
+        &self,
+        request: &mut GpuDataRequest,
+        prim_rect: LayoutRect,
+    ) {
+        match *self {
+            PrimitiveTemplateKind::Clear => {
+                // Opaque black with operator dest out
+                request.push(PremultipliedColorF::BLACK);
+            }
+            PrimitiveTemplateKind::Rectangle { ref color, .. } => {
+                request.push(color.premultiplied());
+            }
+            PrimitiveTemplateKind::NormalBorder { .. } => {
+                // Border primitives currently used for
+                // image borders, and run through the
+                // normal brush_image shader.
+                request.push(PremultipliedColorF::WHITE);
+                request.push(PremultipliedColorF::WHITE);
+                request.push([
+                    prim_rect.size.width,
+                    prim_rect.size.height,
+                    0.0,
+                    0.0,
+                ]);
+            }
+            PrimitiveTemplateKind::ImageBorder { .. } => {
+                // Border primitives currently used for
+                // image borders, and run through the
+                // normal brush_image shader.
+                request.push(PremultipliedColorF::WHITE);
+                request.push(PremultipliedColorF::WHITE);
+                request.push([
+                    prim_rect.size.width,
+                    prim_rect.size.height,
+                    0.0,
+                    0.0,
+                ]);
+            }
+            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::TextRun { ref glyphs, ref font, ref offset, .. } => {
+                request.push(ColorF::from(font.color).premultiplied());
+                // this is the only case where we need to provide plain color to GPU
+                let bg_color = ColorF::from(font.bg_color);
+                request.push([bg_color.r, bg_color.g, bg_color.b, 1.0]);
+                request.push([
+                    offset.x.to_f32_px(),
+                    offset.y.to_f32_px(),
+                    0.0,
+                    0.0,
+                ]);
+
+                let mut gpu_block = [0.0; 4];
+                for (i, src) in glyphs.iter().enumerate() {
+                    // Two glyphs are packed per GPU block.
+
+                    if (i & 1) == 0 {
+                        gpu_block[0] = src.point.x;
+                        gpu_block[1] = src.point.y;
+                    } else {
+                        gpu_block[2] = src.point.x;
+                        gpu_block[3] = src.point.y;
+                        request.push(gpu_block);
+                    }
+                }
+
+                // Ensure the last block is added in the case
+                // of an odd number of glyphs.
+                if (glyphs.len() & 1) != 0 {
+                    request.push(gpu_block);
+                }
+
+                assert!(request.current_used_block_num() <= MAX_VERTEX_TEXTURE_WIDTH);
+            }
+            PrimitiveTemplateKind::YuvImage { color_depth, .. } => {
+                request.push([
+                    color_depth.rescaling_factor(),
+                    0.0,
+                    0.0,
+                    0.0
+                ]);
+            }
+            PrimitiveTemplateKind::Unused => {}
+        }
+    }
+
+    fn write_segment_gpu_blocks(
+        &self,
+        request: &mut GpuDataRequest,
+        prim_rect: LayoutRect,
+    ) {
+        match *self {
+            PrimitiveTemplateKind::Clear => {
+                request.write_segment(
+                    prim_rect,
+                    [0.0; 4],
+                );
+            }
+            PrimitiveTemplateKind::NormalBorder { ref template, .. } => {
+                for segment in &template.brush_segments {
+                    // has to match VECS_PER_SEGMENT
+                    request.write_segment(
+                        segment.local_rect,
+                        segment.extra_data,
+                    );
+                }
+            }
+            PrimitiveTemplateKind::ImageBorder { ref brush_segments, .. } => {
+                for segment in brush_segments {
+                    // has to match VECS_PER_SEGMENT
+                    request.write_segment(
+                        segment.local_rect,
+                        segment.extra_data,
+                    );
+                }
+            }
+            PrimitiveTemplateKind::LineDecoration { .. } => {
+                request.write_segment(
+                    prim_rect,
+                    [0.0; 4],
+                );
+            }
+            PrimitiveTemplateKind::Rectangle { .. } |
+            PrimitiveTemplateKind::TextRun { .. } |
+            PrimitiveTemplateKind::YuvImage { .. } |
+            PrimitiveTemplateKind::Unused => {}
+        }
+    }
+}
+
 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
     /// done if the cache entry is invalid (due to first use or eviction).
     pub fn update(
         &mut self,
         frame_state: &mut FrameBuildingState,
     ) {
+        if let Some(mut request) = frame_state.gpu_cache.request(&mut self.gpu_cache_handle) {
+            self.kind.write_prim_gpu_blocks(&mut request, self.prim_rect);
+            self.kind.write_segment_gpu_blocks(&mut request, self.prim_rect);
+        }
+
         self.opacity = match self.kind {
             PrimitiveTemplateKind::Clear => {
-                if let Some(mut request) = frame_state.gpu_cache.request(&mut self.gpu_cache_handle) {
-                    // Opaque black with operator dest out
-                    request.push(PremultipliedColorF::BLACK);
-
-                    request.write_segment(
-                        self.prim_rect,
-                        [0.0; 4],
-                    );
-                }
-
                 PrimitiveOpacity::translucent()
             }
             PrimitiveTemplateKind::Rectangle { ref color, .. } => {
-                if let Some(mut request) = frame_state.gpu_cache.request(&mut self.gpu_cache_handle) {
-                    request.push(color.premultiplied());
-                }
-
                 PrimitiveOpacity::from_alpha(color.a)
             }
-            PrimitiveTemplateKind::NormalBorder { ref template, .. } => {
-                if let Some(mut request) = frame_state.gpu_cache.request(&mut self.gpu_cache_handle) {
-                    // Border primitives currently used for
-                    // image borders, and run through the
-                    // normal brush_image shader.
-                    request.push(PremultipliedColorF::WHITE);
-                    request.push(PremultipliedColorF::WHITE);
-                    request.push([
-                        self.prim_rect.size.width,
-                        self.prim_rect.size.height,
-                        0.0,
-                        0.0,
-                    ]);
-
-                    for segment in &template.brush_segments {
-                        // has to match VECS_PER_SEGMENT
-                        request.write_segment(
-                            segment.local_rect,
-                            segment.extra_data,
-                        );
-                    }
-                }
-
+            PrimitiveTemplateKind::NormalBorder { .. } => {
                 // Shouldn't matter, since the segment opacity is used instead
                 PrimitiveOpacity::translucent()
             }
-            PrimitiveTemplateKind::ImageBorder { request, ref brush_segments, .. } => {
-                if let Some(mut request) = frame_state.gpu_cache.request(&mut self.gpu_cache_handle) {
-                    // Border primitives currently used for
-                    // image borders, and run through the
-                    // normal brush_image shader.
-                    request.push(PremultipliedColorF::WHITE);
-                    request.push(PremultipliedColorF::WHITE);
-                    request.push([
-                        self.prim_rect.size.width,
-                        self.prim_rect.size.height,
-                        0.0,
-                        0.0,
-                    ]);
-
-                    for segment in brush_segments {
-                        // has to match VECS_PER_SEGMENT
-                        request.write_segment(
-                            segment.local_rect,
-                            segment.extra_data,
-                        );
-                    }
-                }
-
+            PrimitiveTemplateKind::ImageBorder { request, .. } => {
                 let image_properties = frame_state
                     .resource_cache
                     .get_image_properties(request.key);
 
                 if let Some(image_properties) = image_properties {
                     frame_state.resource_cache.request_image(
                         request,
                         frame_state.gpu_cache,
@@ -763,81 +886,36 @@ impl PrimitiveTemplate {
                     PrimitiveOpacity {
                         is_opaque: image_properties.descriptor.is_opaque,
                     }
                 } else {
                     PrimitiveOpacity::opaque()
                 }
             }
             PrimitiveTemplateKind::LineDecoration { ref cache_key, ref color } => {
-                if let Some(mut request) = frame_state.gpu_cache.request(&mut self.gpu_cache_handle) {
-                    // Work out the stretch parameters (for image repeat) based on the
-                    // line decoration parameters.
-
-                    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());
-                        }
-                    }
-
-                    request.write_segment(
-                        self.prim_rect,
-                        [0.0; 4],
-                    );
-                }
-
                 match cache_key {
                     Some(..) => PrimitiveOpacity::translucent(),
                     None => PrimitiveOpacity::from_alpha(color.a),
                 }
             }
-            PrimitiveTemplateKind::TextRun { ref glyphs, ref font, ref offset, .. } => {
-                if let Some(mut request) = frame_state.gpu_cache.request(&mut self.gpu_cache_handle) {
-                    request.push(ColorF::from(font.color).premultiplied());
-                    // this is the only case where we need to provide plain color to GPU
-                    let bg_color = ColorF::from(font.bg_color);
-                    request.push([bg_color.r, bg_color.g, bg_color.b, 1.0]);
-                    request.push([
-                        offset.x.to_f32_px(),
-                        offset.y.to_f32_px(),
-                        0.0,
-                        0.0,
-                    ]);
-
-                    let mut gpu_block = [0.0; 4];
-                    for (i, src) in glyphs.iter().enumerate() {
-                        // Two glyphs are packed per GPU block.
-
-                        if (i & 1) == 0 {
-                            gpu_block[0] = src.point.x;
-                            gpu_block[1] = src.point.y;
-                        } else {
-                            gpu_block[2] = src.point.x;
-                            gpu_block[3] = src.point.y;
-                            request.push(gpu_block);
-                        }
-                    }
-
-                    // Ensure the last block is added in the case
-                    // of an odd number of glyphs.
-                    if (glyphs.len() & 1) != 0 {
-                        request.push(gpu_block);
-                    }
-
-                    assert!(request.current_used_block_num() <= MAX_VERTEX_TEXTURE_WIDTH);
+            PrimitiveTemplateKind::TextRun { .. } => {
+                PrimitiveOpacity::translucent()
+            }
+            PrimitiveTemplateKind::YuvImage { format, yuv_key, image_rendering, .. } => {
+                let channel_num = format.get_plane_num();
+                debug_assert!(channel_num <= 3);
+                for channel in 0 .. channel_num {
+                    frame_state.resource_cache.request_image(
+                        ImageRequest {
+                            key: yuv_key[channel],
+                            rendering: image_rendering,
+                            tile: None,
+                        },
+                        frame_state.gpu_cache,
+                    );
                 }
 
                 PrimitiveOpacity::translucent()
             }
             PrimitiveTemplateKind::Unused => {
                 PrimitiveOpacity::translucent()
             }
         };
@@ -937,23 +1015,16 @@ pub enum BrushKind {
         stretch_size: LayoutSize,
         tile_spacing: LayoutSize,
         color: ColorF,
         source: ImageSource,
         sub_rect: Option<DeviceIntRect>,
         opacity_binding_index: OpacityBindingIndex,
         visible_tiles: Vec<VisibleImageTile>,
     },
-    YuvImage {
-        yuv_key: [ImageKey; 3],
-        format: YuvFormat,
-        color_depth: ColorDepth,
-        color_space: YuvColorSpace,
-        image_rendering: ImageRendering,
-    },
     RadialGradient {
         stops_handle: GpuCacheHandle,
         stops_range: ItemRange<GradientStop>,
         extend_mode: ExtendMode,
         center: LayoutPoint,
         start_radius: f32,
         end_radius: f32,
         ratio_xy: f32,
@@ -981,17 +1052,16 @@ impl BrushKind {
             BrushKind::Image { ref request, .. } => {
                 // tiled images don't support segmentation
                 resource_cache
                     .get_image_properties(request.key)
                     .and_then(|properties| properties.tiling)
                     .is_none()
             }
 
-            BrushKind::YuvImage { .. } |
             BrushKind::RadialGradient { .. } |
             BrushKind::LinearGradient { .. } => true,
         }
     }
 
     // Construct a brush that is an image wisth `stretch_size` dimensions and
     // `color`.
     pub fn new_image(
@@ -1154,24 +1224,16 @@ impl BrushPrimitive {
     fn write_gpu_blocks_if_required(
         &mut self,
         local_rect: LayoutRect,
         gpu_cache: &mut GpuCache,
     ) {
         if let Some(mut request) = gpu_cache.request(&mut self.gpu_location) {
             // has to match VECS_PER_SPECIFIC_BRUSH
             match self.kind {
-                BrushKind::YuvImage { color_depth, .. } => {
-                    request.push([
-                        color_depth.rescaling_factor(),
-                        0.0,
-                        0.0,
-                        0.0
-                    ]);
-                }
                 // Images are drawn as a white color, modulated by the total
                 // opacity coming from any collapsed property bindings.
                 BrushKind::Image { stretch_size, tile_spacing, color, .. } => {
                     request.push(color.premultiplied());
                     request.push(PremultipliedColorF::WHITE);
                     request.push([
                         stretch_size.width + tile_spacing.width,
                         stretch_size.height + tile_spacing.height,
@@ -1781,16 +1843,23 @@ pub enum PrimitiveContainer {
         fill: bool,
         repeat_horizontal: RepeatMode,
         repeat_vertical: RepeatMode,
         outset: SideOffsets2D<f32>,
     },
     Rectangle {
         color: ColorF,
     },
+    YuvImage {
+        color_depth: ColorDepth,
+        yuv_key: [ImageKey; 3],
+        format: YuvFormat,
+        color_space: YuvColorSpace,
+        image_rendering: ImageRendering,
+    },
 }
 
 impl PrimitiveContainer {
     // Return true if the primary primitive is visible.
     // Used to trivially reject non-visible primitives.
     // TODO(gw): Currently, primitives other than those
     //           listed here are handled before the
     //           add_primitive() call. In the future
@@ -1799,25 +1868,25 @@ impl PrimitiveContainer {
     pub fn is_visible(&self) -> bool {
         match *self {
             PrimitiveContainer::TextRun { ref font, .. } => {
                 font.color.a > 0
             }
             PrimitiveContainer::Brush(ref brush) => {
                 match brush.kind {
                     BrushKind::Image { .. } |
-                    BrushKind::YuvImage { .. } |
                     BrushKind::RadialGradient { .. } |
                     BrushKind::LinearGradient { .. } => {
                         true
                     }
                 }
             }
             PrimitiveContainer::NormalBorder { .. } |
             PrimitiveContainer::ImageBorder { .. } |
+            PrimitiveContainer::YuvImage { .. } |
             PrimitiveContainer::Clear => {
                 true
             }
             PrimitiveContainer::Rectangle { ref color, .. } |
             PrimitiveContainer::LineDecoration { ref color, .. } => {
                 color.a > 0.0
             }
         }
@@ -1845,16 +1914,27 @@ impl PrimitiveContainer {
             }
             PrimitiveContainer::Rectangle { color, .. } => {
                 let key = PrimitiveKeyKind::Rectangle {
                     color: color.into(),
                 };
 
                 (key, None)
             }
+            PrimitiveContainer::YuvImage { color_depth, yuv_key, format, color_space, image_rendering, .. } => {
+                let key = PrimitiveKeyKind::YuvImage {
+                    color_depth,
+                    yuv_key,
+                    format,
+                    color_space,
+                    image_rendering,
+                };
+
+                (key, None)
+            }
             PrimitiveContainer::ImageBorder {
                 request,
                 widths,
                 width,
                 height,
                 slice,
                 fill,
                 repeat_vertical,
@@ -2003,28 +2083,26 @@ impl PrimitiveContainer {
                     BrushKind::Image { request, stretch_size, .. } => {
                         PrimitiveContainer::Brush(BrushPrimitive::new(
                             BrushKind::new_image(request.clone(),
                                                  stretch_size.clone(),
                                                  shadow.color),
                             None,
                         ))
                     }
-                    BrushKind::YuvImage { .. } |
                     BrushKind::RadialGradient { .. } |
                     BrushKind::LinearGradient { .. } => {
                         panic!("bug: other brush kinds not expected here yet");
                     }
                 }
             }
-            PrimitiveContainer::ImageBorder { .. } => {
-                panic!("bug: image borders are not supported in shadow contexts");
-            }
+            PrimitiveContainer::ImageBorder { .. } |
+            PrimitiveContainer::YuvImage { .. } |
             PrimitiveContainer::Clear => {
-                panic!("bug: clear rects are not supported in shadow contexts");
+                panic!("bug: this prim is not supported in shadow contexts");
             }
         }
     }
 }
 
 pub enum PrimitiveDetails {
     Brush(BrushPrimitive),
 }
@@ -2072,16 +2150,19 @@ pub enum PrimitiveInstanceKind {
         cache_handles: storage::Range<RenderTaskCacheEntryHandle>,
     },
     ImageBorder {
     },
     Rectangle {
         opacity_binding_index: OpacityBindingIndex,
         segment_instance_index: SegmentInstanceIndex,
     },
+    YuvImage {
+        segment_instance_index: SegmentInstanceIndex,
+    },
     /// Clear out a rect, used for special effects.
     Clear,
 }
 
 #[derive(Clone, Debug)]
 pub struct PrimitiveInstance {
     /// Identifies the kind of primitive this
     /// instance is, and references to where
@@ -2391,21 +2472,18 @@ impl PrimitiveStore {
         match prim_instance.kind {
             PrimitiveInstanceKind::Rectangle { .. } => {
                 return Some(pic_index);
             }
             PrimitiveInstanceKind::Clear |
             PrimitiveInstanceKind::TextRun { .. } |
             PrimitiveInstanceKind::NormalBorder { .. } |
             PrimitiveInstanceKind::ImageBorder { .. } |
-            PrimitiveInstanceKind::LineDecoration { .. } => {
-                // TODO: Once rectangles and/or images are ported
-                //       to use interned primitives, we will need
-                //       to handle opacity collapse here.
-            }
+            PrimitiveInstanceKind::YuvImage { .. } |
+            PrimitiveInstanceKind::LineDecoration { .. } => {}
             PrimitiveInstanceKind::Picture { pic_index } => {
                 let pic = &self.pictures[pic_index.0];
 
                 // If we encounter a picture that is a pass-through
                 // (i.e. no composite mode), then we can recurse into
                 // that to try and find a primitive to collapse to.
                 if pic.requested_composite_mode.is_none() {
                     return self.get_opacity_collapse_prim(pic_index);
@@ -2416,17 +2494,16 @@ impl PrimitiveStore {
                 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::Image { .. } => {
                                 return Some(pic_index)
                             }
-                            BrushKind::YuvImage { .. } |
                             BrushKind::LinearGradient { .. } |
                             BrushKind::RadialGradient { .. } => {}
                         }
                     }
                 }
             }
         }
 
@@ -2474,17 +2551,16 @@ impl PrimitiveStore {
                                 match brush.kind {
                                     BrushKind::Image { ref mut opacity_binding_index, .. } => {
                                         if *opacity_binding_index == OpacityBindingIndex::INVALID {
                                             *opacity_binding_index = self.opacity_bindings.push(OpacityBinding::new());
                                         }
                                         let opacity_binding = &mut self.opacity_bindings[*opacity_binding_index];
                                         opacity_binding.push(binding);
                                     }
-                                    BrushKind::YuvImage { .. } |
                                     BrushKind::LinearGradient { .. } |
                                     BrushKind::RadialGradient { .. } => {
                                         unreachable!("bug: invalid prim type for opacity collapse");
                                     }
                                 }
                             }
                         }
                     }
@@ -2553,16 +2629,17 @@ impl PrimitiveStore {
                     }
                 }
                 PrimitiveInstanceKind::TextRun { .. } |
                 PrimitiveInstanceKind::Rectangle { .. } |
                 PrimitiveInstanceKind::LineDecoration { .. } |
                 PrimitiveInstanceKind::LegacyPrimitive { .. } |
                 PrimitiveInstanceKind::NormalBorder { .. } |
                 PrimitiveInstanceKind::ImageBorder { .. } |
+                PrimitiveInstanceKind::YuvImage { .. } |
                 PrimitiveInstanceKind::Clear => {
                     None
                 }
             }
         };
 
         let (is_passthrough, clip_node_collector) = match pic_info {
             Some((pic_context_for_children, mut pic_state_for_children, mut prim_list)) => {
@@ -2605,16 +2682,17 @@ impl PrimitiveStore {
                 let pic = &self.pictures[pic_index.0];
                 (pic.local_rect, LayoutRect::max_rect())
             }
             PrimitiveInstanceKind::TextRun { .. } |
             PrimitiveInstanceKind::Clear |
             PrimitiveInstanceKind::NormalBorder { .. } |
             PrimitiveInstanceKind::ImageBorder { .. } |
             PrimitiveInstanceKind::Rectangle { .. } |
+            PrimitiveInstanceKind::YuvImage { .. } |
             PrimitiveInstanceKind::LineDecoration { .. } => {
                 let prim_data = &resources
                     .prim_data_store[prim_instance.prim_data_handle];
                 (prim_data.prim_rect, prim_data.clip_rect)
             }
             PrimitiveInstanceKind::LegacyPrimitive { prim_index } => {
                 let prim = &self.primitives[prim_index.0];
                 (prim.local_rect, prim.local_clip_rect)
@@ -2753,16 +2831,22 @@ impl PrimitiveStore {
             }
         }
 
         #[cfg(debug_assertions)]
         {
             prim_instance.prepared_frame_id = frame_state.render_tasks.frame_id();
         }
 
+        pic_state.is_cacheable &= prim_instance.is_cacheable(
+            &self.primitives,
+            &resources.prim_data_store,
+            frame_state.resource_cache,
+        );
+
         match prim_instance.kind {
             PrimitiveInstanceKind::Picture { pic_index } => {
                 let pic = &mut self.pictures[pic_index.0];
                 if pic.prepare_for_render(
                     pic_index,
                     prim_instance,
                     &prim_local_rect,
                     clipped_world_rect,
@@ -2799,16 +2883,17 @@ impl PrimitiveStore {
                     );
                 }
             }
             PrimitiveInstanceKind::TextRun { .. } |
             PrimitiveInstanceKind::Clear |
             PrimitiveInstanceKind::Rectangle { .. } |
             PrimitiveInstanceKind::NormalBorder { .. } |
             PrimitiveInstanceKind::ImageBorder { .. } |
+            PrimitiveInstanceKind::YuvImage { .. } |
             PrimitiveInstanceKind::LineDecoration { .. } => {
                 self.prepare_interned_prim_for_render(
                     prim_instance,
                     prim_context,
                     pic_context,
                     frame_context,
                     frame_state,
                     resources,
@@ -2818,17 +2903,16 @@ impl PrimitiveStore {
             PrimitiveInstanceKind::LegacyPrimitive { prim_index } => {
                 let prim_details = &mut self.primitives[prim_index.0].details;
 
                 prim_instance.prepare_prim_for_render_inner(
                     prim_local_rect,
                     prim_details,
                     prim_context,
                     pic_context,
-                    pic_state,
                     frame_context,
                     frame_state,
                     display_list,
                     &mut self.opacity_bindings,
                 );
             }
         }
 
@@ -2950,17 +3034,17 @@ impl PrimitiveStore {
         // Update the template this instane references, which may refresh the GPU
         // cache with any shared template data.
         prim_data.update(
             frame_state,
         );
 
         let is_chased = prim_instance.is_chased();
 
-        match (&mut prim_instance.kind, &mut prim_data.kind) {
+        let segment_instance_index = match (&mut prim_instance.kind, &mut prim_data.kind) {
             (
                 PrimitiveInstanceKind::LineDecoration { ref mut cache_handle, .. },
                 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);
                 }
@@ -2997,16 +3081,18 @@ impl PrimitiveStore {
                                 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
                         }
                     ));
                 }
+
+                SegmentInstanceIndex::UNUSED
             }
             (
                 PrimitiveInstanceKind::TextRun { run_index, .. },
                 PrimitiveTemplateKind::TextRun { ref font, ref glyphs, .. }
             ) => {
                 // The transform only makes sense for screen space rasterization
                 let transform = prim_context.spatial_node.world_content_transform.to_transform();
 
@@ -3022,23 +3108,26 @@ impl PrimitiveStore {
                     &transform,
                     pic_context,
                     frame_state.resource_cache,
                     frame_state.gpu_cache,
                     frame_state.render_tasks,
                     frame_state.special_render_passes,
                     scratch,
                 );
+
+                SegmentInstanceIndex::UNUSED
             }
             (
                 PrimitiveInstanceKind::Clear,
                 PrimitiveTemplateKind::Clear
             ) => {
                 // Nothing specific to prepare for clear rects, since the
                 // GPU cache is updated by the template earlier.
+                SegmentInstanceIndex::UNUSED
             }
             (
                 PrimitiveInstanceKind::NormalBorder { ref mut cache_handles, .. },
                 PrimitiveTemplateKind::NormalBorder { template, .. }
             ) => {
                 // TODO(gw): When drawing in screen raster mode, we should also incorporate a
                 //           scale factor from the world transform to get an appropriately
                 //           sized border task.
@@ -3085,52 +3174,64 @@ impl PrimitiveStore {
                             task_id
                         }
                     ));
                 }
 
                 *cache_handles = scratch
                     .border_cache_handles
                     .extend(handles);
+
+                SegmentInstanceIndex::UNUSED
             }
             (
                 PrimitiveInstanceKind::ImageBorder { .. },
                 PrimitiveTemplateKind::ImageBorder { .. }
             ) => {
+                SegmentInstanceIndex::UNUSED
             }
             (
                 PrimitiveInstanceKind::Rectangle { segment_instance_index, opacity_binding_index, .. },
-                PrimitiveTemplateKind::Rectangle { ref color, .. }
+                PrimitiveTemplateKind::Rectangle { .. }
             ) => {
-                if *segment_instance_index != SegmentInstanceIndex::UNUSED {
-                    let segment_instance = &mut scratch.segment_instances[*segment_instance_index];
-
-                    if let Some(mut request) = frame_state.gpu_cache.request(&mut segment_instance.gpu_cache_handle) {
-                        let segments = &scratch.segments[segment_instance.segments_range];
-
-                        request.push(color.premultiplied());
-
-                        for segment in segments {
-                            request.write_segment(
-                                segment.local_rect,
-                                [0.0; 4],
-                            );
-                        }
-                    }
-                }
-
                 update_opacity_binding(
                     &mut self.opacity_bindings,
                     *opacity_binding_index,
                     frame_context.scene_properties,
                 );
+
+                *segment_instance_index
+            }
+            (
+                PrimitiveInstanceKind::YuvImage { segment_instance_index, .. },
+                PrimitiveTemplateKind::YuvImage { .. }
+            ) => {
+                *segment_instance_index
             }
             _ => {
                 unreachable!();
             }
+        };
+
+        debug_assert!(segment_instance_index != SegmentInstanceIndex::INVALID);
+        if segment_instance_index != SegmentInstanceIndex::UNUSED {
+            let segment_instance = &mut scratch.segment_instances[segment_instance_index];
+
+            if let Some(mut request) = frame_state.gpu_cache.request(&mut segment_instance.gpu_cache_handle) {
+                let segments = &scratch.segments[segment_instance.segments_range];
+
+                prim_data.kind.write_prim_gpu_blocks(&mut request, prim_data.prim_rect);
+
+                for segment in segments {
+                    request.write_segment(
+                        segment.local_rect,
+                        [0.0; 4],
+                    );
+                }
+            }
         }
     }
 }
 
 fn build_gradient_stops_request(
     stops_handle: &mut GpuCacheHandle,
     stops_range: ItemRange<GradientStop>,
     reverse_stops: bool,
@@ -3381,17 +3482,18 @@ impl PrimitiveInstance {
         prim_local_clip_rect: LayoutRect,
         prim_clip_chain: &ClipChainInstance,
         frame_state: &mut FrameBuildingState,
         primitives: &mut [Primitive],
         resources: &FrameResources,
         scratch: &mut PrimitiveScratchBuffer,
     ) {
         match self.kind {
-            PrimitiveInstanceKind::Rectangle { ref mut segment_instance_index, .. } => {
+            PrimitiveInstanceKind::Rectangle { ref mut segment_instance_index, .. } |
+            PrimitiveInstanceKind::YuvImage { ref mut segment_instance_index, .. } => {
                 if *segment_instance_index == SegmentInstanceIndex::INVALID {
                     let mut segments: SmallVec<[BrushSegment; 8]> = SmallVec::new();
 
                     if write_brush_segment_description(
                         prim_local_rect,
                         prim_local_clip_rect,
                         prim_clip_chain,
                         &mut frame_state.segment_builder,
@@ -3498,16 +3600,17 @@ impl PrimitiveInstance {
     ) -> bool {
         let segments = match self.kind {
             PrimitiveInstanceKind::Picture { .. } |
             PrimitiveInstanceKind::TextRun { .. } |
             PrimitiveInstanceKind::Clear |
             PrimitiveInstanceKind::LineDecoration { .. } => {
                 return false;
             }
+            PrimitiveInstanceKind::YuvImage { segment_instance_index, .. } |
             PrimitiveInstanceKind::Rectangle { segment_instance_index, .. } => {
                 debug_assert!(segment_instance_index != SegmentInstanceIndex::INVALID);
 
                 if segment_instance_index == SegmentInstanceIndex::UNUSED {
                     return false;
                 }
 
                 let segment_instance = &scratch.segment_instances[segment_instance_index];
@@ -3627,29 +3730,23 @@ impl PrimitiveInstance {
     }
 
     fn prepare_prim_for_render_inner(
         &mut self,
         prim_local_rect: LayoutRect,
         prim_details: &mut PrimitiveDetails,
         prim_context: &PrimitiveContext,
         pic_context: &PictureContext,
-        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,
-        );
-
         match *prim_details {
             PrimitiveDetails::Brush(ref mut brush) => {
                 brush.opacity = match brush.kind {
                     BrushKind::Image {
                         request,
                         sub_rect,
                         stretch_size,
                         color,
@@ -3858,32 +3955,16 @@ impl PrimitiveInstance {
                                 PrimitiveOpacity::from_alpha(current_opacity * color.a)
                             } else {
                                 PrimitiveOpacity::translucent()
                             }
                         } else {
                             PrimitiveOpacity::opaque()
                         }
                     }
-                    BrushKind::YuvImage { format, yuv_key, image_rendering, .. } => {
-                        let channel_num = format.get_plane_num();
-                        debug_assert!(channel_num <= 3);
-                        for channel in 0 .. channel_num {
-                            frame_state.resource_cache.request_image(
-                                ImageRequest {
-                                    key: yuv_key[channel],
-                                    rendering: image_rendering,
-                                    tile: None,
-                                },
-                                frame_state.gpu_cache,
-                            );
-                        }
-
-                        PrimitiveOpacity::opaque()
-                    }
                     BrushKind::RadialGradient {
                         stops_range,
                         center,
                         start_radius,
                         end_radius,
                         ratio_xy,
                         extend_mode,
                         stretch_size,
--- a/gfx/wr/webrender/src/surface.rs
+++ b/gfx/wr/webrender/src/surface.rs
@@ -235,16 +235,17 @@ impl SurfaceDescriptor {
             // For now, we only handle interned primitives. If we encounter
             // a legacy primitive or picture, then fail to create a cache
             // descriptor.
             match prim_instance.kind {
                 PrimitiveInstanceKind::Picture { .. } |
                 PrimitiveInstanceKind::LegacyPrimitive { .. } => {
                     return None;
                 }
+                PrimitiveInstanceKind::YuvImage { .. } |
                 PrimitiveInstanceKind::LineDecoration { .. } |
                 PrimitiveInstanceKind::TextRun { .. } |
                 PrimitiveInstanceKind::NormalBorder { .. } |
                 PrimitiveInstanceKind::Rectangle { .. } |
                 PrimitiveInstanceKind::ImageBorder { .. } |
                 PrimitiveInstanceKind::Clear => {}
             }