Bug 1514180 - Update webrender to commit d2c673ada607f29846c3d1ac8ca7d2b272ba1b2d (WR PR #3413). r=kats
authorWR Updater Bot <graphics-team@mozilla.staktrace.com>
Fri, 14 Dec 2018 13:32:32 +0000
changeset 507639 6b2cb8f39f1511209ca576f55fedbd8345a61f8c
parent 507638 877799c69d918ba1f2a18b2a44967a86f0c32f5c
child 507640 ae1660029d89b816a51e9f6d5f39f01849ef6466
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
bugs1514180
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 1514180 - Update webrender to commit d2c673ada607f29846c3d1ac8ca7d2b272ba1b2d (WR PR #3413). r=kats https://github.com/servo/webrender/pull/3413 Differential Revision: https://phabricator.services.mozilla.com/D14538
gfx/webrender_bindings/revision.txt
gfx/wr/webrender/src/display_list_flattener.rs
gfx/wr/webrender/src/picture.rs
--- a/gfx/webrender_bindings/revision.txt
+++ b/gfx/webrender_bindings/revision.txt
@@ -1,1 +1,1 @@
-b8096a76030818c960a6111666c8b1213b5da25a
+d2c673ada607f29846c3d1ac8ca7d2b272ba1b2d
--- a/gfx/wr/webrender/src/display_list_flattener.rs
+++ b/gfx/wr/webrender/src/display_list_flattener.rs
@@ -151,27 +151,16 @@ pub struct DisplayListFlattener<'a> {
 
     /// Reference to the document resources, which contains
     /// shared (interned) data between display lists.
     resources: &'a mut DocumentResources,
 
     /// The root picture index for this flattener. This is the picture
     /// to start the culling phase from.
     pub root_pic_index: PictureIndex,
-
-    /// TODO(gw): This is a complete hack that relies on knowledge of
-    ///           what the Gecko display list looks like. It's used
-    ///           for now to work out which scroll root to use to
-    ///           create the picture cache for the content. It's only
-    ///           ever used if picture caching is enabled in the
-    ///           RendererOptions struct. We will need to work out
-    ///           a better API to avoid this, before we enable it
-    ///           for all users. Another alternative is that this
-    ///           will disappear itself when document splitting is used.
-    picture_cache_scroll_root: Option<SpatialNodeIndex>,
 }
 
 impl<'a> DisplayListFlattener<'a> {
     pub fn create_frame_builder(
         scene: &Scene,
         clip_scroll_tree: &mut ClipScrollTree,
         font_instances: FontInstanceMap,
         view: &DocumentView,
@@ -199,38 +188,31 @@ impl<'a> DisplayListFlattener<'a> {
             hit_testing_runs: Vec::new(),
             pending_shadow_items: VecDeque::new(),
             sc_stack: Vec::new(),
             pipeline_clip_chain_stack: vec![ClipChainId::NONE],
             prim_store: PrimitiveStore::new(&prim_store_stats),
             clip_store: ClipStore::new(),
             resources,
             root_pic_index: PictureIndex(0),
-            picture_cache_scroll_root: None,
         };
 
         flattener.push_root(
             root_pipeline_id,
             &root_pipeline.viewport_size,
             &root_pipeline.content_size,
         );
 
         flattener.flatten_root(
             root_pipeline,
             &root_pipeline.viewport_size,
         );
 
         debug_assert!(flattener.sc_stack.is_empty());
 
-        // If picture caching is enabled, splice up the root
-        // stacking context to enable correct surface caching.
-        flattener.setup_picture_caching(
-            root_pipeline_id,
-        );
-
         new_scene.root_pipeline_id = Some(root_pipeline_id);
         new_scene.pipeline_epochs = scene.pipeline_epochs.clone();
         new_scene.pipelines = scene.pipelines.clone();
 
         FrameBuilder::with_display_list_flattener(
             view.inner_rect,
             background_color,
             view.window_size,
@@ -240,17 +222,17 @@ impl<'a> DisplayListFlattener<'a> {
 
     /// Cut the primitives in the root stacking context based on the picture
     /// caching scroll root. This is a temporary solution for the initial
     /// implementation of picture caching. We need to work out the specifics
     /// of how WR should decide (or Gecko should communicate) where the main
     /// content frame is that should get surface caching.
     fn setup_picture_caching(
         &mut self,
-        root_pipeline_id: PipelineId,
+        primitives: &mut Vec<PrimitiveInstance>,
     ) {
         if !self.config.enable_picture_caching {
             return;
         }
 
         // This method is basically a hack to set up picture caching in a minimal
         // way without having to check the public API (yet). The intent is to
         // work out a good API for this and switch to using it. In the mean
@@ -271,108 +253,112 @@ impl<'a> DisplayListFlattener<'a> {
         //     [primitives attached to cached scroll root]
         //  [trailing root primitives]
         //
         // This step is typically very quick, because there are only
         // a small number of items in the root stacking context, since
         // most of the content is embedded in its own picture.
         //
 
-        // See if we found a scroll root for the cached surface root.
-        if let Some(picture_cache_scroll_root) = self.picture_cache_scroll_root {
-            // Get the list of existing primitives in the main stacking context.
-            let mut old_prim_list = mem::replace(
-                &mut self.prim_store.pictures[self.root_pic_index.0].prim_list,
-                PrimitiveList::empty(),
+        // Find the first primitive which has the desired scroll root.
+        let mut main_scroll_root = None;
+
+        let first_index = primitives.iter().position(|instance| {
+            let scroll_root = self.find_scroll_root(
+                instance.spatial_node_index,
             );
 
-            // Find the first primitive which has the desired scroll root.
-            let first_index = old_prim_list.prim_instances.iter().position(|instance| {
-                let scroll_root = self.find_scroll_root(
-                    instance.spatial_node_index,
-                );
-
-                scroll_root == picture_cache_scroll_root
-            }).unwrap_or(old_prim_list.prim_instances.len());
+            if scroll_root == ROOT_SPATIAL_NODE_INDEX {
+                false
+            } else {
+                debug_assert!(main_scroll_root.is_none());
+                main_scroll_root = Some(scroll_root);
+                true
+            }
+        }).unwrap_or(primitives.len());
 
-            // Split off the preceding primtives.
-            let mut remaining_prims = old_prim_list.prim_instances.split_off(first_index);
-
-            // Find the first primitive in reverse order that is not the root scroll node.
-            let last_index = remaining_prims.iter().rposition(|instance| {
-                let scroll_root = self.find_scroll_root(
-                    instance.spatial_node_index,
-                );
+        let main_scroll_root = match main_scroll_root {
+            Some(main_scroll_root) => main_scroll_root,
+            None => ROOT_SPATIAL_NODE_INDEX,
+        };
 
-                scroll_root != ROOT_SPATIAL_NODE_INDEX
-            }).unwrap_or(remaining_prims.len() - 1);
-
-            let preceding_prims = old_prim_list.prim_instances;
-            let trailing_prims = remaining_prims.split_off(last_index + 1);
+        // Get the list of existing primitives in the main stacking context.
+        let mut old_prim_list = mem::replace(
+            primitives,
+            Vec::new(),
+        );
 
-            let prim_list = PrimitiveList::new(
-                remaining_prims,
-                &self.resources,
-            );
+        // Split off the preceding primtives.
+        let mut remaining_prims = old_prim_list.split_off(first_index);
 
-            // 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(
-                true,
-                LayoutSize::zero(),
-                LayoutRect::max_rect(),
-                PrimitiveKeyKind::Unused,
+        // Find the first primitive in reverse order that is not the root scroll node.
+        let last_index = remaining_prims.iter().rposition(|instance| {
+            let scroll_root = self.find_scroll_root(
+                instance.spatial_node_index,
             );
 
-            let primitive_data_handle = self.resources
-                .prim_interner
-                .intern(&prim_key, || {
-                    PrimitiveSceneData {
-                        prim_relative_clip_rect: LayoutRect::max_rect(),
-                        prim_size: LayoutSize::zero(),
-                        is_backface_visible: true,
-                    }
-                }
-            );
+            scroll_root != ROOT_SPATIAL_NODE_INDEX
+        }).unwrap_or(remaining_prims.len() - 1);
+
+        let preceding_prims = old_prim_list;
+        let trailing_prims = remaining_prims.split_off(last_index + 1);
+
+        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(
+            true,
+            LayoutSize::zero(),
+            LayoutRect::max_rect(),
+            PrimitiveKeyKind::Unused,
+        );
 
-            let pic_index = self.prim_store.pictures.alloc().init(PicturePrimitive::new_image(
-                Some(PictureCompositeMode::TileCache { clear_color: ColorF::new(1.0, 1.0, 1.0, 1.0) }),
-                Picture3DContext::Out,
-                root_pipeline_id,
-                None,
-                true,
-                RasterSpace::Screen,
-                prim_list,
-                picture_cache_scroll_root,
-                LayoutRect::max_rect(),
-                &self.clip_store,
-            ));
+        let primitive_data_handle = self.resources
+            .prim_interner
+            .intern(&prim_key, || {
+                PrimitiveSceneData {
+                    prim_relative_clip_rect: LayoutRect::max_rect(),
+                    prim_size: LayoutSize::zero(),
+                    is_backface_visible: true,
+                }
+            }
+        );
 
-            let instance = PrimitiveInstance::new(
-                LayoutPoint::zero(),
-                PrimitiveInstanceKind::Picture {
-                    data_handle: primitive_data_handle,
-                    pic_index: PictureIndex(pic_index)
-                },
-                ClipChainId::NONE,
-                picture_cache_scroll_root,
-            );
+        let pic_index = self.prim_store.pictures.alloc().init(PicturePrimitive::new_image(
+            Some(PictureCompositeMode::TileCache { clear_color: ColorF::new(1.0, 1.0, 1.0, 1.0) }),
+            Picture3DContext::Out,
+            self.scene.root_pipeline_id.unwrap(),
+            None,
+            true,
+            RasterSpace::Screen,
+            prim_list,
+            main_scroll_root,
+            LayoutRect::max_rect(),
+            &self.clip_store,
+        ));
 
-            // This contains the tile caching picture, with preceding and
-            // trailing primitives outside the main scroll root.
-            let mut new_prim_list = preceding_prims;
-            new_prim_list.push(instance);
-            new_prim_list.extend(trailing_prims);
+        let instance = PrimitiveInstance::new(
+            LayoutPoint::zero(),
+            PrimitiveInstanceKind::Picture {
+                data_handle: primitive_data_handle,
+                pic_index: PictureIndex(pic_index)
+            },
+            ClipChainId::NONE,
+            main_scroll_root,
+        );
 
-            // Finally, store the sliced primitive list in the root picture.
-            self.prim_store.pictures[self.root_pic_index.0].prim_list = PrimitiveList::new(
-                new_prim_list,
-                &self.resources,
-            );
-        }
+        // This contains the tile caching picture, with preceding and
+        // trailing primitives outside the main scroll root.
+        primitives.extend(preceding_prims);
+        primitives.push(instance);
+        primitives.extend(trailing_prims);
     }
 
     /// Find the spatial node that is the scroll root for a given
     /// spatial node.
     fn find_scroll_root(
         &self,
         spatial_node_index: SpatialNodeIndex,
     ) -> SpatialNodeIndex {
@@ -551,34 +537,26 @@ impl<'a> DisplayListFlattener<'a> {
         // positioning offsets.
         let frame_rect = item.clip_rect().translate(reference_frame_relative_offset);
         let content_rect = item.rect().translate(reference_frame_relative_offset);
 
         debug_assert!(info.clip_id != info.scroll_frame_id);
 
         self.add_clip_node(info.clip_id, clip_and_scroll_ids, clip_region);
 
-        let node_index = self.add_scroll_frame(
+        self.add_scroll_frame(
             info.scroll_frame_id,
             info.clip_id,
             info.external_id,
             pipeline_id,
             &frame_rect,
             &content_rect.size,
             info.scroll_sensitivity,
             ScrollFrameKind::Explicit,
         );
-
-        // TODO(gw): See description of picture_cache_scroll_root field for information
-        //           about this temporary hack. What it's trying to identify is the first
-        //           scroll root within the first iframe that we encounter in the display
-        //           list.
-        if self.picture_cache_scroll_root.is_none() && pipeline_id != self.scene.root_pipeline_id.unwrap() {
-            self.picture_cache_scroll_root = Some(node_index);
-        }
     }
 
     fn flatten_reference_frame(
         &mut self,
         traversal: &mut BuiltDisplayListIter<'a>,
         pipeline_id: PipelineId,
         item: &DisplayItemRef,
         reference_frame: &ReferenceFrame,
@@ -1223,16 +1201,19 @@ impl<'a> DisplayListFlattener<'a> {
         // Check if this stacking context is the root of a pipeline, and the caller
         // has requested it as an output frame.
         let frame_output_pipeline_id = if is_pipeline_root && self.output_pipelines.contains(&pipeline_id) {
             Some(pipeline_id)
         } else {
             None
         };
 
+        let create_tile_cache = is_pipeline_root &&
+                                self.sc_stack.len() == 2;
+
         // Get the transform-style of the parent stacking context,
         // which determines if we *might* need to draw this on
         // an intermediate surface for plane splitting purposes.
         let (parent_is_3d, extra_3d_instance) = match self.sc_stack.last_mut() {
             Some(sc) => {
                 // Cut the sequence of flat children before starting a child stacking context,
                 // so that the relative order between them and our current SC is preserved.
                 let extra_instance = sc.cut_flat_item_sequence(
@@ -1312,21 +1293,22 @@ impl<'a> DisplayListFlattener<'a> {
             requested_raster_space,
             spatial_node_index,
             clip_chain_id,
             frame_output_pipeline_id,
             composite_ops,
             should_isolate,
             transform_style,
             context_3d,
+            create_tile_cache,
         });
     }
 
     pub fn pop_stacking_context(&mut self) {
-        let stacking_context = self.sc_stack.pop().unwrap();
+        let mut stacking_context = self.sc_stack.pop().unwrap();
 
         // If we encounter a stacking context that is effectively a no-op, then instead
         // of creating a picture, just append the primitive list to the parent stacking
         // context as a short cut. This serves two purposes:
         // (a) It's an optimization to reduce picture count and allocations, as display lists
         //     often contain a lot of these stacking contexts that don't require pictures or
         //     off-screen surfaces.
         // (b) It's useful for the initial version of picture caching in gecko, by enabling
@@ -1337,16 +1319,22 @@ impl<'a> DisplayListFlattener<'a> {
                 parent_sc,
                 self.clip_scroll_tree,
             ) {
                 parent_sc.primitives.extend(stacking_context.primitives);
                 return;
             }
         }
 
+        if stacking_context.create_tile_cache {
+            self.setup_picture_caching(
+                &mut stacking_context.primitives,
+            );
+        }
+
         // An arbitrary large clip rect. For now, we don't
         // specify a clip specific to the stacking context.
         // However, now that they are represented as Picture
         // primitives, we can apply any kind of clip mask
         // to them, as for a normal primitive. This is needed
         // to correctly handle some CSS cases (see #1957).
         let max_clip = LayoutRect::max_rect();
 
@@ -2528,16 +2516,19 @@ struct FlattenedStackingContext {
     /// Pipeline this stacking context belongs to.
     pipeline_id: PipelineId,
 
     /// CSS transform-style property.
     transform_style: TransformStyle,
 
     /// Defines the relationship to a preserve-3D hiearachy.
     context_3d: Picture3DContext<PrimitiveInstance>,
+
+    /// If true, create a tile cache for this stacking context.
+    create_tile_cache: bool,
 }
 
 impl FlattenedStackingContext {
     /// Return true if the stacking context has a valid preserve-3d property
     pub fn is_3d(&self) -> bool {
         self.transform_style == TransformStyle::Preserve3D && self.composite_ops.is_empty()
     }
 
@@ -2577,16 +2568,21 @@ impl FlattenedStackingContext {
             return false;
         }
 
         // If represents a transform, it may affect backface visibility of children
         if !clip_scroll_tree.node_is_identity(self.spatial_node_index) {
             return false;
         }
 
+        // If the pipelines are different, we care for purposes of selecting tile caches
+        if self.pipeline_id != parent.pipeline_id {
+            return false;
+        }
+
         // It is redundant!
         true
     }
 
     /// For a Preserve3D context, cut the sequence of the immediate flat children
     /// recorded so far and generate a picture from them.
     pub fn cut_flat_item_sequence(
         &mut self,
--- a/gfx/wr/webrender/src/picture.rs
+++ b/gfx/wr/webrender/src/picture.rs
@@ -15,17 +15,17 @@ use euclid::approxeq::ApproxEq;
 use intern::ItemUid;
 use internal_types::{FastHashMap, PlaneSplitter};
 use frame_builder::{FrameBuildingContext, FrameBuildingState, PictureState, PictureContext};
 use gpu_cache::{GpuCache, 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, CoordinateSpaceMapping};
+use prim_store::{get_raster_rects, CoordinateSpaceMapping, PointKey};
 use prim_store::{OpacityBindingStorage, PrimitiveTemplateKind, ImageInstanceStorage, OpacityBindingIndex, SizeKey};
 use render_backend::FrameResources;
 use render_task::{ClearMode, RenderTask, RenderTaskCacheEntryHandle, TileBlit};
 use render_task::{RenderTaskCacheKey, RenderTaskCacheKeyKind, RenderTaskId, RenderTaskLocation};
 use resource_cache::ResourceCache;
 use scene::{FilterOpHelpers, SceneProperties};
 use scene_builder::DocumentResources;
 use smallvec::SmallVec;
@@ -73,17 +73,18 @@ pub struct TileCoordinate;
 // Geometry types for tile coordinates.
 pub type TileOffset = TypedPoint2D<i32, TileCoordinate>;
 pub type TileSize = TypedSize2D<i32, TileCoordinate>;
 pub type TileRect = TypedRect<i32, TileCoordinate>;
 
 /// The size in device pixels of a cached tile. The currently chosen
 /// size is arbitrary. We should do some profiling to find the best
 /// size for real world pages.
-pub const TILE_SIZE_DP: i32 = 512;
+pub const TILE_SIZE_WIDTH: i32 = 1024;
+pub const TILE_SIZE_HEIGHT: i32 = 256;
 
 /// Information about the state of a transform dependency.
 #[derive(Debug)]
 pub struct TileTransformInfo {
     /// The spatial node in the current clip-scroll tree that
     /// this transform maps to.
     spatial_node_index: SpatialNodeIndex,
     /// Tiles check this to see if the dependencies have changed.
@@ -242,23 +243,37 @@ impl Tile {
         }
     }
 }
 
 /// Index of a transform array local to the tile.
 #[derive(Debug, Copy, Clone, Eq, PartialEq, Hash)]
 pub struct TileTransformIndex(u32);
 
+/// Defines a key that uniquely identifies a primitive instance.
+#[derive(Debug, Eq, PartialEq, Hash)]
+pub struct PrimitiveDescriptor {
+    /// Uniquely identifies the content of the primitive template.
+    prim_uid: ItemUid,
+    /// The origin in local space of this primitive.
+    origin: PointKey,
+    /// The first clip in the clip_uids array of clips that affect this tile.
+    first_clip: u16,
+    /// The number of clips that affect this primitive instance.
+    clip_count: u16,
+}
+
 /// Uniquely describes the content of this tile, in a way that can be
 /// (reasonably) efficiently hashed and compared.
 #[derive(Debug, Eq, PartialEq, Hash)]
 pub struct TileDescriptor {
-    /// List of primitive unique identifiers. The uid is guaranteed
-    /// to uniquely describe the content of the primitive.
-    pub prim_uids: Vec<ItemUid>,
+    /// List of primitive instance unique identifiers. The uid is guaranteed
+    /// to uniquely describe the content of the primitive template, while
+    /// the other parameters describe the clip chain and instance params.
+    pub prims: Vec<PrimitiveDescriptor>,
 
     /// List of clip node unique identifiers. The uid is guaranteed
     /// to uniquely describe the content of the clip node.
     pub clip_uids: Vec<ItemUid>,
 
     /// List of local tile transform ids that are used to position
     /// the primitive and clip items above.
     pub transform_ids: Vec<TileTransformIndex>,
@@ -283,31 +298,31 @@ pub struct TileDescriptor {
 
 impl TileDescriptor {
     fn new(
         tile_offset: TileOffset,
         local_tile_size: SizeKey,
         raster_transform: TransformKey,
     ) -> Self {
         TileDescriptor {
-            prim_uids: Vec::new(),
+            prims: Vec::new(),
             clip_uids: Vec::new(),
             transform_ids: Vec::new(),
             opacity_bindings: Vec::new(),
             transforms: Vec::new(),
             tile_offset,
             raster_transform,
             local_tile_size,
         }
     }
 
     /// Clear the dependency information for a tile, when the dependencies
     /// are being rebuilt.
     fn clear(&mut self) {
-        self.prim_uids.clear();
+        self.prims.clear();
         self.clip_uids.clear();
         self.transform_ids.clear();
         self.transforms.clear();
         self.opacity_bindings.clear();
     }
 }
 
 /// Represents the dirty region of a tile cache picture.
@@ -411,18 +426,18 @@ impl TileCache {
         );
 
         // Work out the local space size of each tile, based on the
         // device pixel size of tiles.
         // TODO(gw): Perhaps add a map_point API to SpaceMapper.
         let world_tile_rect = WorldRect::from_floats(
             0.0,
             0.0,
-            TILE_SIZE_DP as f32 / frame_context.device_pixel_scale.0,
-            TILE_SIZE_DP as f32 / frame_context.device_pixel_scale.0,
+            TILE_SIZE_WIDTH as f32 / frame_context.device_pixel_scale.0,
+            TILE_SIZE_HEIGHT as f32 / frame_context.device_pixel_scale.0,
         );
         let local_tile_rect = world_mapper
             .unmap(&world_tile_rect)
             .expect("bug: unable to get local tile size");
         self.local_tile_size = local_tile_rect.size;
         self.local_origin = pic_rect.origin;
 
         // Walk the transforms and see if we need to rebuild the primitive
@@ -551,25 +566,42 @@ impl TileCache {
             }
 
             // Reset the size of the tile grid.
             self.tile_rect = TileRect::zero();
         }
 
         // Get the tile coordinates in the picture space.
         let pic_rect = TypedRect::from_untyped(&pic_rect.to_untyped());
-        let local_pic_rect = pic_rect.translate(&-self.local_origin.to_vector());
-
-        let x0 = (local_pic_rect.origin.x / self.local_tile_size.width).floor() as i32;
-        let y0 = (local_pic_rect.origin.y / self.local_tile_size.height).floor() as i32;
-        let x1 = ((local_pic_rect.origin.x + local_pic_rect.size.width) / self.local_tile_size.width).ceil() as i32;
-        let y1 = ((local_pic_rect.origin.y + local_pic_rect.size.height) / self.local_tile_size.height).ceil() as i32;
+        let (p0, p1) = self.get_tile_coords_for_rect(&pic_rect);
 
         // Update the tile array allocation if needed.
-        self.reconfigure_tiles_if_required(x0, y0, x1, y1);
+        self.reconfigure_tiles_if_required(p0.x, p0.y, p1.x, p1.y);
+    }
+
+    /// Get the tile coordinates for a given rectangle.
+    fn get_tile_coords_for_rect(
+        &self,
+        rect: &LayoutRect,
+    ) -> (TileOffset, TileOffset) {
+        // Translate the rectangle into the virtual tile space
+        let origin = rect.origin - self.local_origin;
+
+        // Get the tile coordinates in the picture space.
+        let p0 = TileOffset::new(
+            (origin.x / self.local_tile_size.width).floor() as i32,
+            (origin.y / self.local_tile_size.height).floor() as i32,
+        );
+
+        let p1 = TileOffset::new(
+            ((origin.x + rect.size.width) / self.local_tile_size.width).ceil() as i32,
+            ((origin.y + rect.size.height) / self.local_tile_size.height).ceil() as i32,
+        );
+
+        (p0, p1)
     }
 
     /// Resize the 2D tiles array if needed in order to fit dependencies
     /// for a given primitive.
     fn reconfigure_tiles_if_required(
         &mut self,
         mut x0: i32,
         mut y0: i32,
@@ -687,24 +719,18 @@ impl TileCache {
         };
 
         // If the rect is invalid, no need to create dependencies.
         // TODO(gw): Need to handle pictures with filters here.
         if rect.size.width <= 0.0 || rect.size.height <= 0.0 {
             return;
         }
 
-        // Translate the rectangle into the virtual tile space
-        let origin = rect.origin - self.local_origin;
-
         // Get the tile coordinates in the picture space.
-        let x0 = (origin.x / self.local_tile_size.width).floor() as i32;
-        let y0 = (origin.y / self.local_tile_size.height).floor() as i32;
-        let x1 = ((origin.x + rect.size.width) / self.local_tile_size.width).ceil() as i32;
-        let y1 = ((origin.y + rect.size.height) / self.local_tile_size.height).ceil() as i32;
+        let (p0, p1) = self.get_tile_coords_for_rect(&rect);
 
         // Build the list of resources that this primitive has dependencies on.
         let mut opacity_bindings: SmallVec<[PropertyBindingId; 4]> = SmallVec::new();
         let mut clip_chain_spatial_nodes: SmallVec<[SpatialNodeIndex; 8]> = SmallVec::new();
         let mut clip_chain_uids: SmallVec<[ItemUid; 8]> = SmallVec::new();
         let mut image_keys: SmallVec<[ImageKey; 8]> = SmallVec::new();
         let mut current_clip_chain_id = prim_instance.clip_chain_id;
 
@@ -790,18 +816,18 @@ impl TileCache {
                 clip_chain_spatial_nodes.push(clip_chain_node.spatial_node_index);
                 clip_chain_uids.push(clip_chain_node.handle.uid());
             }
             current_clip_chain_id = clip_chain_node.parent_clip_chain_id;
         }
 
         // Normalize the tile coordinates before adding to tile dependencies.
         // For each affected tile, mark any of the primitive dependencies.
-        for y in y0 - self.tile_rect.origin.y .. y1 - self.tile_rect.origin.y {
-            for x in x0 - self.tile_rect.origin.x .. x1 - self.tile_rect.origin.x {
+        for y in p0.y - self.tile_rect.origin.y .. p1.y - self.tile_rect.origin.y {
+            for x in p0.x - self.tile_rect.origin.x .. p1.x - self.tile_rect.origin.x {
                 let index = (y * self.tile_rect.size.width + x) as usize;
                 let tile = &mut self.tiles[index];
 
                 // Mark if the tile is cacheable at all.
                 tile.is_cacheable &= is_cacheable;
                 tile.in_use = true;
 
                 // Include any image keys this tile depends on.
@@ -830,17 +856,22 @@ impl TileCache {
                 // Include any opacity bindings this primitive depends on.
                 for id in &opacity_bindings {
                     if tile.opacity_bindings.insert(*id) {
                         tile.descriptor.opacity_bindings.push(*id);
                     }
                 }
 
                 // Update the tile descriptor, used for tile comparison during scene swaps.
-                tile.descriptor.prim_uids.push(prim_instance.uid());
+                tile.descriptor.prims.push(PrimitiveDescriptor {
+                    prim_uid: prim_instance.uid(),
+                    origin: prim_instance.prim_origin.into(),
+                    first_clip: tile.descriptor.clip_uids.len() as u16,
+                    clip_count: clip_chain_uids.len() as u16,
+                });
                 tile.descriptor.clip_uids.extend_from_slice(&clip_chain_uids);
             }
         }
     }
 
     /// Get a local space rectangle for a given tile coordinate.
     pub fn get_tile_rect(&self, x: i32, y: i32) -> LayoutRect {
         LayoutRect::new(
@@ -2179,18 +2210,18 @@ impl PicturePrimitive {
                 match tile_cache.dirty_region {
                     Some(ref dirty_region) => {
                         // Texture cache descriptor for each tile.
                         // TODO(gw): If / when we start to use tile caches with
                         //           clip masks and/or transparent backgrounds,
                         //           we will need to correctly select an opacity
                         //           here and a blend mode in batch.rs.
                         let descriptor = ImageDescriptor::new(
-                            TILE_SIZE_DP,
-                            TILE_SIZE_DP,
+                            TILE_SIZE_WIDTH,
+                            TILE_SIZE_HEIGHT,
                             ImageFormat::BGRA8,
                             true,
                             false,
                         );
 
                         // Get a picture rect, expanded to tile boundaries.
                         let p0 = pic_rect.origin;
                         let p1 = pic_rect.bottom_right();
@@ -2230,18 +2261,18 @@ impl PicturePrimitive {
 
                                     let cache_item = frame_state
                                         .resource_cache
                                         .get_texture_cache_item(&tile.handle);
 
                                     // Set up the blit command now that we know where the dest
                                     // rect is in the texture cache.
                                     let offset = DeviceIntPoint::new(
-                                        (x - dirty_region.tile_offset.x) * TILE_SIZE_DP,
-                                        (y - dirty_region.tile_offset.y) * TILE_SIZE_DP,
+                                        (x - dirty_region.tile_offset.x) * TILE_SIZE_WIDTH,
+                                        (y - dirty_region.tile_offset.y) * TILE_SIZE_HEIGHT,
                                     );
 
                                     blits.push(TileBlit {
                                         target: cache_item,
                                         offset,
                                     });
 
                                     tile.is_valid = true;