Bug 1530518 - Refactor the way stacking context coords are converted to reference frame coords. r=emilio
authorGlenn Watson <github@intuitionlibrary.com>
Mon, 25 Feb 2019 22:31:06 +0000
changeset 518903 eec82a970303374d6aab0430050016d20fe20ab9
parent 518902 d69f9928a349d8b6c8bdccb2f689f3171ed293d5
child 518904 825dfac611b25553f36ee0da6d7e5b043087b7e3
push id10862
push userffxbld-merge
push dateMon, 11 Mar 2019 13:01:11 +0000
treeherdermozilla-beta@a2e7f5c935da [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersemilio
bugs1530518
milestone67.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 1530518 - Refactor the way stacking context coords are converted to reference frame coords. r=emilio This doesn't introduce any functional changes. However, it refactors the way that stacking context coords are converted into reference frame relative coordinates. Having a single method to retrieve the current offset will make it easier to take advantage of the newly added API that allows Gecko to supply initial scroll offsets for scroll nodes. In turn, this will allow WR to normalize the local coordinates of primitives, which will allow future improvements and simplifications to the picture caching implementation. Differential Revision: https://phabricator.services.mozilla.com/D21090
gfx/wr/webrender/src/border.rs
gfx/wr/webrender/src/box_shadow.rs
gfx/wr/webrender/src/display_list_flattener.rs
--- a/gfx/wr/webrender/src/border.rs
+++ b/gfx/wr/webrender/src/border.rs
@@ -1,16 +1,16 @@
 /* This Source Code Form is subject to the terms of the Mozilla Public
  * License, v. 2.0. If a copy of the MPL was not distributed with this
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 use api::{BorderRadius, BorderSide, BorderStyle, ColorF, ColorU, DeviceRect, DeviceSize};
 use api::{LayoutSideOffsets, LayoutSizeAu, LayoutPrimitiveInfo, LayoutToDeviceScale};
 use api::{DeviceVector2D, DevicePoint, LayoutRect, LayoutSize, DeviceIntSize};
-use api::{AuHelpers, LayoutPoint, LayoutPointAu, RepeatMode, TexelRect, LayoutVector2D};
+use api::{AuHelpers, LayoutPoint, LayoutPointAu, RepeatMode, TexelRect};
 use api::NormalBorder as ApiNormalBorder;
 use ellipse::Ellipse;
 use euclid::vec2;
 use display_list_flattener::DisplayListFlattener;
 use gpu_types::{BorderInstance, BorderSegment, BrushFlags};
 use prim_store::{BorderSegmentInfo, BrushSegment, NinePatchDescriptor};
 use prim_store::{EdgeAaSegmentMask, ScrollNodeAndClipChain};
 use prim_store::borders::NormalBorderPrim;
@@ -211,30 +211,28 @@ pub fn ensure_no_corner_overlap(
 
 impl<'a> DisplayListFlattener<'a> {
     pub fn add_normal_border(
         &mut self,
         info: &LayoutPrimitiveInfo,
         border: &ApiNormalBorder,
         widths: LayoutSideOffsets,
         clip_and_scroll: ScrollNodeAndClipChain,
-        reference_frame_relative_offset: LayoutVector2D,
     ) {
         let mut border = *border;
         ensure_no_corner_overlap(&mut border.radius, info.rect.size);
 
         self.add_primitive(
             clip_and_scroll,
             info,
             Vec::new(),
             NormalBorderPrim {
                 border: border.into(),
                 widths: widths.to_au(),
             },
-            reference_frame_relative_offset,
         );
     }
 }
 
 pub trait BorderSideHelpers {
     fn border_color(&self, is_inner_border: bool) -> ColorF;
 }
 
--- a/gfx/wr/webrender/src/box_shadow.rs
+++ b/gfx/wr/webrender/src/box_shadow.rs
@@ -72,17 +72,16 @@ impl<'a> DisplayListFlattener<'a> {
         clip_and_scroll: ScrollNodeAndClipChain,
         prim_info: &LayoutPrimitiveInfo,
         box_offset: &LayoutVector2D,
         color: ColorF,
         mut blur_radius: f32,
         spread_radius: f32,
         border_radius: BorderRadius,
         clip_mode: BoxShadowClipMode,
-        reference_frame_relative_offset: LayoutVector2D,
     ) {
         if color.a == 0.0 {
             return;
         }
 
         // Inset shadows get smaller as spread radius increases.
         let (spread_amount, prim_clip_mode) = match clip_mode {
             BoxShadowClipMode::Outset => (spread_radius, ClipMode::ClipOut),
@@ -162,17 +161,16 @@ impl<'a> DisplayListFlattener<'a> {
 
             self.add_primitive(
                 clip_and_scroll,
                 &LayoutPrimitiveInfo::with_clip_rect(final_prim_rect, prim_info.clip_rect),
                 clips,
                 PrimitiveKeyKind::Rectangle {
                     color: color.into(),
                 },
-                reference_frame_relative_offset,
             );
         } else {
             // Normal path for box-shadows with a valid blur radius.
             let blur_offset = (BLUR_SAMPLE_SCALE * blur_radius).ceil();
             let mut extra_clips = vec![];
 
             // Add a normal clip mask to clip out the contents
             // of the surrounding primitive.
@@ -252,17 +250,16 @@ impl<'a> DisplayListFlattener<'a> {
                 }
             };
 
             self.add_primitive(
                 clip_and_scroll,
                 &prim_info,
                 extra_clips,
                 prim,
-                reference_frame_relative_offset,
             );
         }
     }
 }
 
 fn adjust_border_radius_for_box_shadow(radius: BorderRadius, spread_amount: f32) -> BorderRadius {
     BorderRadius {
         top_left: adjust_corner_for_box_shadow(radius.top_left, spread_amount),
--- a/gfx/wr/webrender/src/display_list_flattener.rs
+++ b/gfx/wr/webrender/src/display_list_flattener.rs
@@ -54,16 +54,88 @@ impl ClipNode {
     fn new(id: ClipChainId, count: usize) -> Self {
         ClipNode {
             id,
             count,
         }
     }
 }
 
+/// The offset stack for a given reference frame.
+struct ReferenceFrameState {
+    /// A stack of current offsets from the current reference frame scope.
+    offsets: Vec<LayoutVector2D>,
+}
+
+/// Maps from stacking context layout coordinates into reference frame
+/// relative coordinates.
+struct ReferenceFrameMapper {
+    /// A stack of reference frame scopes.
+    frames: Vec<ReferenceFrameState>,
+}
+
+impl ReferenceFrameMapper {
+    fn new() -> Self {
+        ReferenceFrameMapper {
+            frames: vec![
+                ReferenceFrameState {
+                    offsets: vec![
+                        LayoutVector2D::zero(),
+                    ],
+                }
+            ],
+        }
+    }
+
+    /// Push a new scope. This resets the current offset to zero, and is
+    /// used when a new reference frame or iframe is pushed.
+    fn push_scope(&mut self) {
+        self.frames.push(ReferenceFrameState {
+            offsets: vec![
+                LayoutVector2D::zero(),
+            ],
+        });
+    }
+
+    /// Pop a reference frame scope off the stack.
+    fn pop_scope(&mut self) {
+        self.frames.pop().unwrap();
+    }
+
+    /// Push a new offset for the current scope. This is used when
+    /// a new stacking context is pushed.
+    fn push_offset(&mut self, offset: LayoutVector2D) {
+        let frame = self.frames.last_mut().unwrap();
+        let current_offset = *frame.offsets.last().unwrap();
+        frame.offsets.push(current_offset + offset);
+    }
+
+    /// Pop a local stacking context offset from the current scope.
+    fn pop_offset(&mut self) {
+        let frame = self.frames.last_mut().unwrap();
+        frame.offsets.pop().unwrap();
+    }
+
+    /// Retrieve the current offset to allow converting a stacking context
+    /// relative coordinate to be relative to the owing reference frame.
+    /// TODO(gw): We could perhaps have separate coordinate spaces for this,
+    ///           however that's going to either mean a lot of changes to
+    ///           public API code, or a lot of changes to internal code.
+    ///           Before doing that, we should revisit how Gecko would
+    ///           prefer to provide coordinates.
+    /// TODO(gw): For now, this includes only the reference frame relative
+    ///           offset. Soon, we will expand this to include the initial
+    ///           scroll offsets that are now available on scroll nodes. This
+    ///           will allow normalizing the coordinates even between display
+    ///           lists where APZ has scrolled the content.
+    fn current_offset(&self) -> LayoutVector2D {
+        *self.frames.last().unwrap().offsets.last().unwrap()
+    }
+}
+
 /// A data structure that keeps track of mapping between API Ids for clips/spatials and the indices
 /// used internally in the ClipScrollTree to avoid having to do HashMap lookups. NodeIdToIndexMapper
 /// is responsible for mapping both ClipId to ClipChainIndex and SpatialId to SpatialNodeIndex.
 #[derive(Default)]
 pub struct NodeIdToIndexMapper {
     clip_node_map: FastHashMap<ClipId, ClipNode>,
     spatial_node_map: FastHashMap<SpatialId, SpatialNodeIndex>,
 }
@@ -141,16 +213,19 @@ pub struct DisplayListFlattener<'a> {
     pub config: FrameBuilderConfig,
 
     /// Reference to the set of data that is interned across display lists.
     interners: &'a mut Interners,
 
     /// The root picture index for this flattener. This is the picture
     /// to start the culling phase from.
     pub root_pic_index: PictureIndex,
+
+    /// Helper struct to map stacking context coords <-> reference frame coords.
+    rf_mapper: ReferenceFrameMapper,
 }
 
 impl<'a> DisplayListFlattener<'a> {
     pub fn create_frame_builder(
         scene: &Scene,
         clip_scroll_tree: &mut ClipScrollTree,
         font_instances: FontInstanceMap,
         view: &DocumentView,
@@ -178,16 +253,17 @@ 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(),
             interners,
             root_pic_index: PictureIndex(0),
+            rf_mapper: ReferenceFrameMapper::new(),
         };
 
         flattener.push_root(
             root_pipeline_id,
             &root_pipeline.viewport_size,
             &root_pipeline.content_size,
         );
 
@@ -211,17 +287,16 @@ impl<'a> DisplayListFlattener<'a> {
             ROOT_SPATIAL_NODE_INDEX,
             ClipChainId::NONE,
             RasterSpace::Screen,
         );
 
         flattener.flatten_items(
             &mut root_pipeline.display_list.iter(),
             root_pipeline.pipeline_id,
-            LayoutVector2D::zero(),
             true,
         );
 
         flattener.pop_stacking_context();
 
         debug_assert!(flattener.sc_stack.is_empty());
 
         new_scene.root_pipeline_id = Some(root_pipeline_id);
@@ -463,17 +538,16 @@ impl<'a> DisplayListFlattener<'a> {
             .get_display_list_for_pipeline(pipeline_id)
             .get(items)
     }
 
     fn flatten_items(
         &mut self,
         traversal: &mut BuiltDisplayListIter<'a>,
         pipeline_id: PipelineId,
-        reference_frame_relative_offset: LayoutVector2D,
         apply_pipeline_clip: bool,
     ) {
         loop {
             let subtraversal = {
                 let item = match traversal.next() {
                     Some(item) => item,
                     None => break,
                 };
@@ -482,17 +556,16 @@ impl<'a> DisplayListFlattener<'a> {
                     SpecificDisplayItem::PopReferenceFrame |
                     SpecificDisplayItem::PopStackingContext => return,
                     _ => (),
                 }
 
                 self.flatten_item(
                     item,
                     pipeline_id,
-                    reference_frame_relative_offset,
                     apply_pipeline_clip,
                 )
             };
 
             // If flatten_item created a sub-traversal, we need `traversal` to have the
             // same state as the completed subtraversal, so we reinitialize it here.
             if let Some(subtraversal) = subtraversal {
                 *traversal = subtraversal;
@@ -500,19 +573,19 @@ impl<'a> DisplayListFlattener<'a> {
         }
     }
 
     fn flatten_sticky_frame(
         &mut self,
         item: &DisplayItemRef,
         info: &StickyFrameDisplayItem,
         parent_node_index: SpatialNodeIndex,
-        reference_frame_relative_offset: &LayoutVector2D,
     ) {
-        let frame_rect = item.rect().translate(reference_frame_relative_offset);
+        let current_offset = self.rf_mapper.current_offset();
+        let frame_rect = item.rect().translate(&current_offset);
         let sticky_frame_info = StickyFrameInfo::new(
             frame_rect,
             info.margins,
             info.vertical_offset_bounds,
             info.horizontal_offset_bounds,
             info.previously_applied_offset,
         );
 
@@ -525,24 +598,24 @@ impl<'a> DisplayListFlattener<'a> {
     }
 
     fn flatten_scroll_frame(
         &mut self,
         item: &DisplayItemRef,
         info: &ScrollFrameDisplayItem,
         parent_node_index: SpatialNodeIndex,
         pipeline_id: PipelineId,
-        reference_frame_relative_offset: &LayoutVector2D,
     ) {
         let complex_clips = self.get_complex_clips(pipeline_id, item.complex_clip().0);
+        let current_offset = self.rf_mapper.current_offset();
         let clip_region = ClipRegion::create_for_clip_node(
             *item.clip_rect(),
             complex_clips,
             info.image_mask,
-            reference_frame_relative_offset,
+            &current_offset,
         );
         // Just use clip rectangle as the frame rect for this scroll frame.
         // This is useful when calculating scroll extents for the
         // SpatialNode::scroll(..) API as well as for properly setting sticky
         // positioning offsets.
         let frame_rect = clip_region.main;
         let content_size = item.rect().size;
 
@@ -562,42 +635,47 @@ impl<'a> DisplayListFlattener<'a> {
 
     fn flatten_reference_frame(
         &mut self,
         traversal: &mut BuiltDisplayListIter<'a>,
         pipeline_id: PipelineId,
         parent_spatial_node: SpatialNodeIndex,
         origin: LayoutPoint,
         reference_frame: &ReferenceFrame,
-        reference_frame_relative_offset: LayoutVector2D,
         apply_pipeline_clip: bool,
     ) {
+        let current_offset = self.rf_mapper.current_offset();
         self.push_reference_frame(
             reference_frame.id,
             Some(parent_spatial_node),
             pipeline_id,
             reference_frame.transform_style,
             reference_frame.transform,
             reference_frame.kind,
-            reference_frame_relative_offset + origin.to_vector(),
+            current_offset + origin.to_vector(),
         );
 
-        self.flatten_items(traversal, pipeline_id, LayoutVector2D::zero(), apply_pipeline_clip);
+        self.rf_mapper.push_scope();
+        self.flatten_items(
+            traversal,
+            pipeline_id,
+            apply_pipeline_clip,
+        );
+        self.rf_mapper.pop_scope();
     }
 
 
     fn flatten_stacking_context(
         &mut self,
         traversal: &mut BuiltDisplayListIter<'a>,
         pipeline_id: PipelineId,
         stacking_context: &StackingContext,
         spatial_node_index: SpatialNodeIndex,
         origin: LayoutPoint,
         filters: ItemRange<FilterOp>,
-        reference_frame_relative_offset: &LayoutVector2D,
         is_backface_visible: bool,
         apply_pipeline_clip: bool,
     ) {
         // Avoid doing unnecessary work for empty stacking contexts.
         if traversal.current_stacking_context_empty() {
             traversal.skip_current_stacking_context();
             return;
         }
@@ -641,54 +719,55 @@ impl<'a> DisplayListFlattener<'a> {
                     found_root = true;
                     break;
                 }
                 cur_clip = self.clip_store.get_clip_chain(cur_clip).parent_clip_chain_id;
             }
             debug_assert!(found_root);
         }
 
+        self.rf_mapper.push_offset(origin.to_vector());
         self.flatten_items(
             traversal,
             pipeline_id,
-            *reference_frame_relative_offset + origin.to_vector(),
             apply_pipeline_clip && clip_chain_id == ClipChainId::NONE,
         );
+        self.rf_mapper.pop_offset();
 
         self.pop_stacking_context();
     }
 
     fn flatten_iframe(
         &mut self,
         item: &DisplayItemRef,
         info: &IframeDisplayItem,
         spatial_node_index: SpatialNodeIndex,
-        reference_frame_relative_offset: &LayoutVector2D,
     ) {
         let iframe_pipeline_id = info.pipeline_id;
         let pipeline = match self.scene.pipelines.get(&iframe_pipeline_id) {
             Some(pipeline) => pipeline,
             None => {
                 debug_assert!(info.ignore_missing_pipeline);
                 return
             },
         };
 
+        let current_offset = self.rf_mapper.current_offset();
         let clip_chain_index = self.add_clip_node(
             ClipId::root(iframe_pipeline_id),
             item.space_and_clip_info(),
             ClipRegion::create_for_clip_node_with_local_clip(
                 item.clip_rect(),
-                reference_frame_relative_offset,
+                &current_offset,
             ),
         );
         self.pipeline_clip_chain_stack.push(clip_chain_index);
 
         let bounds = item.rect();
-        let origin = *reference_frame_relative_offset + bounds.origin.to_vector();
+        let origin = current_offset + bounds.origin.to_vector();
         let spatial_node_index = self.push_reference_frame(
             SpatialId::root_reference_frame(iframe_pipeline_id),
             Some(spatial_node_index),
             iframe_pipeline_id,
             TransformStyle::Flat,
             PropertyBinding::Value(LayoutTransform::identity()),
             ReferenceFrameKind::Transform,
             origin,
@@ -701,108 +780,104 @@ impl<'a> DisplayListFlattener<'a> {
             Some(ExternalScrollId(0, iframe_pipeline_id)),
             iframe_pipeline_id,
             &iframe_rect,
             &pipeline.content_size,
             ScrollSensitivity::ScriptAndInputEvents,
             ScrollFrameKind::PipelineRoot,
         );
 
+        self.rf_mapper.push_scope();
         self.flatten_items(
             &mut pipeline.display_list.iter(),
             pipeline.pipeline_id,
-            LayoutVector2D::zero(),
             true,
         );
+        self.rf_mapper.pop_scope();
 
         self.pipeline_clip_chain_stack.pop();
     }
 
     fn flatten_item<'b>(
         &'b mut self,
         item: DisplayItemRef<'a, 'b>,
         pipeline_id: PipelineId,
-        reference_frame_relative_offset: LayoutVector2D,
         apply_pipeline_clip: bool,
     ) -> Option<BuiltDisplayListIter<'a>> {
         let space_and_clip = item.space_and_clip_info();
         let clip_and_scroll = ScrollNodeAndClipChain::new(
             self.id_to_index_mapper.get_spatial_node_index(space_and_clip.spatial_id),
             if !apply_pipeline_clip && space_and_clip.clip_id.is_root() {
                 ClipChainId::NONE
             } else if space_and_clip.clip_id.is_valid() {
                 self.id_to_index_mapper.get_clip_chain_id(space_and_clip.clip_id)
             } else {
                 ClipChainId::INVALID
             },
         );
-        let prim_info = item.get_layout_primitive_info(&reference_frame_relative_offset);
+        let prim_info = item.get_layout_primitive_info(
+            &self.rf_mapper.current_offset(),
+        );
 
         match *item.item() {
             SpecificDisplayItem::Image(ref info) => {
                 self.add_image(
                     clip_and_scroll,
                     &prim_info,
                     info.stretch_size,
                     info.tile_spacing,
                     None,
                     info.image_key,
                     info.image_rendering,
                     info.alpha_type,
                     info.color,
-                    reference_frame_relative_offset,
                 );
             }
             SpecificDisplayItem::YuvImage(ref info) => {
                 self.add_yuv_image(
                     clip_and_scroll,
                     &prim_info,
                     info.yuv_data,
                     info.color_depth,
                     info.color_space,
                     info.image_rendering,
-                    reference_frame_relative_offset,
                 );
             }
             SpecificDisplayItem::Text(ref text_info) => {
                 self.add_text(
                     clip_and_scroll,
-                    reference_frame_relative_offset,
                     &prim_info,
                     &text_info.font_key,
                     &text_info.color,
                     item.glyphs(),
                     text_info.glyph_options,
                     pipeline_id,
                 );
             }
             SpecificDisplayItem::Rectangle(ref info) => {
                 self.add_solid_rectangle(
                     clip_and_scroll,
                     &prim_info,
                     info.color,
-                    reference_frame_relative_offset,
                 );
             }
             SpecificDisplayItem::ClearRectangle => {
                 self.add_clear_rectangle(
                     clip_and_scroll,
                     &prim_info,
-                    reference_frame_relative_offset,
                 );
             }
             SpecificDisplayItem::Line(ref info) => {
                 self.add_line(
                     clip_and_scroll,
                     &prim_info,
                     info.wavy_line_thickness,
                     info.orientation,
                     info.color,
                     info.style,
-                    reference_frame_relative_offset,
                 );
             }
             SpecificDisplayItem::Gradient(ref info) => {
                 if let Some(prim_key_kind) = self.create_linear_gradient_prim(
                     &prim_info,
                     info.gradient.start_point,
                     info.gradient.end_point,
                     item.gradient_stops(),
@@ -812,17 +887,16 @@ impl<'a> DisplayListFlattener<'a> {
                     pipeline_id,
                     None,
                 ) {
                     self.add_nonshadowable_primitive(
                         clip_and_scroll,
                         &prim_info,
                         Vec::new(),
                         prim_key_kind,
-                        reference_frame_relative_offset,
                     );
                 }
             }
             SpecificDisplayItem::RadialGradient(ref info) => {
                 let prim_key_kind = self.create_radial_gradient_prim(
                     &prim_info,
                     info.gradient.center,
                     info.gradient.start_offset * info.gradient.radius.width,
@@ -835,90 +909,86 @@ impl<'a> DisplayListFlattener<'a> {
                     pipeline_id,
                     None,
                 );
                 self.add_nonshadowable_primitive(
                     clip_and_scroll,
                     &prim_info,
                     Vec::new(),
                     prim_key_kind,
-                    reference_frame_relative_offset,
                 );
             }
             SpecificDisplayItem::BoxShadow(ref box_shadow_info) => {
+                let current_offset = self.rf_mapper.current_offset();
                 let bounds = box_shadow_info
                     .box_bounds
-                    .translate(&reference_frame_relative_offset);
+                    .translate(&current_offset);
                 let mut prim_info = prim_info.clone();
                 prim_info.rect = bounds;
                 self.add_box_shadow(
                     clip_and_scroll,
                     &prim_info,
                     &box_shadow_info.offset,
                     box_shadow_info.color,
                     box_shadow_info.blur_radius,
                     box_shadow_info.spread_radius,
                     box_shadow_info.border_radius,
                     box_shadow_info.clip_mode,
-                    reference_frame_relative_offset,
                 );
             }
             SpecificDisplayItem::Border(ref info) => {
                 self.add_border(
                     clip_and_scroll,
                     &prim_info,
                     info,
                     item.gradient_stops(),
                     pipeline_id,
-                    reference_frame_relative_offset,
                 );
             }
             SpecificDisplayItem::PushStackingContext(ref info) => {
                 let mut subtraversal = item.sub_iter();
                 self.flatten_stacking_context(
                     &mut subtraversal,
                     pipeline_id,
                     &info.stacking_context,
                     clip_and_scroll.spatial_node_index,
                     item.rect().origin,
                     item.filters(),
-                    &reference_frame_relative_offset,
                     prim_info.is_backface_visible,
                     apply_pipeline_clip,
                 );
                 return Some(subtraversal);
             }
             SpecificDisplayItem::PushReferenceFrame(ref info) => {
                 let mut subtraversal = item.sub_iter();
                 self.flatten_reference_frame(
                     &mut subtraversal,
                     pipeline_id,
                     clip_and_scroll.spatial_node_index,
                     item.rect().origin,
                     &info.reference_frame,
-                    reference_frame_relative_offset,
                     apply_pipeline_clip,
                 );
                 return Some(subtraversal);
             }
             SpecificDisplayItem::Iframe(ref info) => {
                 self.flatten_iframe(
                     &item,
                     info,
                     clip_and_scroll.spatial_node_index,
-                    &reference_frame_relative_offset,
                 );
             }
             SpecificDisplayItem::Clip(ref info) => {
+                let current_offset = self.rf_mapper.current_offset();
                 let complex_clips = self.get_complex_clips(pipeline_id, item.complex_clip().0);
                 let clip_region = ClipRegion::create_for_clip_node(
                     *item.clip_rect(),
                     complex_clips,
                     info.image_mask,
-                    &reference_frame_relative_offset,
+                    &current_offset,
                 );
                 self.add_clip_node(info.id, space_and_clip, clip_region);
             }
             SpecificDisplayItem::ClipChain(ref info) => {
                 // For a user defined clip-chain the parent (if specified) must
                 // refer to another user defined clip-chain. If none is specified,
                 // the parent is the root clip-chain for the given pipeline. This
                 // is used to provide a root clip chain for iframes.
@@ -987,46 +1057,45 @@ impl<'a> DisplayListFlattener<'a> {
                 self.id_to_index_mapper.add_clip_chain(ClipId::ClipChain(info.id), clip_chain_id, 0);
             },
             SpecificDisplayItem::ScrollFrame(ref info) => {
                 self.flatten_scroll_frame(
                     &item,
                     info,
                     clip_and_scroll.spatial_node_index,
                     pipeline_id,
-                    &reference_frame_relative_offset,
                 );
             }
             SpecificDisplayItem::StickyFrame(ref info) => {
                 self.flatten_sticky_frame(
                     &item,
                     info,
                     clip_and_scroll.spatial_node_index,
-                    &reference_frame_relative_offset,
                 );
             }
 
             // Do nothing; these are dummy items for the display list parser
             SpecificDisplayItem::SetGradientStops => {}
 
             SpecificDisplayItem::PopReferenceFrame |
             SpecificDisplayItem::PopStackingContext => {
                 unreachable!("Should have returned in parent method.")
             }
             SpecificDisplayItem::PushShadow(shadow) => {
                 self.push_shadow(shadow, clip_and_scroll);
             }
             SpecificDisplayItem::PopAllShadows => {
-                self.pop_all_shadows(reference_frame_relative_offset);
+                self.pop_all_shadows();
             }
             SpecificDisplayItem::PushCacheMarker(_marker) => {
             }
             SpecificDisplayItem::PopCacheMarker => {
             }
         }
+
         None
     }
 
     // Given a list of clip sources, a positioning node and
     // a parent clip chain, return a new clip chain entry.
     // If the supplied list of clip sources is empty, then
     // just return the parent clip chain id directly.
     fn build_clip_chain(
@@ -1065,17 +1134,16 @@ impl<'a> DisplayListFlattener<'a> {
     ///
     /// TODO(djg): Can this inline into `add_interned_prim_to_draw_list`
     fn create_primitive<P>(
         &mut self,
         info: &LayoutPrimitiveInfo,
         clip_chain_id: ClipChainId,
         spatial_node_index: SpatialNodeIndex,
         prim: P,
-        reference_frame_relative_offset: LayoutVector2D,
     ) -> PrimitiveInstance
     where
         P: Internable<InternData=PrimitiveSceneData>,
         P::Source: AsInstanceKind<Handle<P::Marker>> + InternDebug,
         Interners: InternerMut<P>,
     {
         // Build a primitive key.
         let prim_key = prim.build_key(info);
@@ -1085,20 +1153,22 @@ impl<'a> DisplayListFlattener<'a> {
             interner
             .intern(&prim_key, || {
                 PrimitiveSceneData {
                     prim_size: info.rect.size,
                     is_backface_visible: info.is_backface_visible,
                 }
             });
 
+        let current_offset = self.rf_mapper.current_offset();
+
         let instance_kind = prim_key.as_instance_kind(
             prim_data_handle,
             &mut self.prim_store,
-            reference_frame_relative_offset,
+            current_offset,
         );
 
         PrimitiveInstance::new(
             info.rect.origin,
             info.clip_rect,
             instance_kind,
             clip_chain_id,
             spatial_node_index,
@@ -1144,17 +1214,16 @@ impl<'a> DisplayListFlattener<'a> {
     /// Convenience interface that creates a primitive entry and adds it
     /// to the draw list.
     fn add_nonshadowable_primitive<P>(
         &mut self,
         clip_and_scroll: ScrollNodeAndClipChain,
         info: &LayoutPrimitiveInfo,
         clip_items: Vec<(LayoutPoint, ClipItemKey)>,
         prim: P,
-        reference_frame_relative_offset: LayoutVector2D,
     )
     where
         P: Internable<InternData = PrimitiveSceneData> + IsVisible,
         P::Source: AsInstanceKind<Handle<P::Marker>> + InternDebug,
         Interners: InternerMut<P>,
     {
         if prim.is_visible() {
             let clip_chain_id = self.build_clip_chain(
@@ -1162,44 +1231,41 @@ impl<'a> DisplayListFlattener<'a> {
                 clip_and_scroll.spatial_node_index,
                 clip_and_scroll.clip_chain_id,
             );
             self.add_prim_to_draw_list(
                 info,
                 clip_chain_id,
                 clip_and_scroll,
                 prim,
-                reference_frame_relative_offset,
             );
         }
     }
 
     pub fn add_primitive<P>(
         &mut self,
         clip_and_scroll: ScrollNodeAndClipChain,
         info: &LayoutPrimitiveInfo,
         clip_items: Vec<(LayoutPoint, ClipItemKey)>,
         prim: P,
-        reference_frame_relative_offset: LayoutVector2D,
     )
     where
         P: Internable<InternData = PrimitiveSceneData> + IsVisible,
         P::Source: AsInstanceKind<Handle<P::Marker>> + InternDebug,
         Interners: InternerMut<P>,
         ShadowItem: From<PendingPrimitive<P>>
     {
         // If a shadow context is not active, then add the primitive
         // directly to the parent picture.
         if self.pending_shadow_items.is_empty() {
             self.add_nonshadowable_primitive(
                 clip_and_scroll,
                 info,
                 clip_items,
                 prim,
-                reference_frame_relative_offset,
             );
         } else {
             debug_assert!(clip_items.is_empty(), "No per-prim clips expected for shadowed primitives");
 
             // There is an active shadow context. Store as a pending primitive
             // for processing during pop_all_shadows.
             self.pending_shadow_items.push_back(PendingPrimitive {
                 clip_and_scroll,
@@ -1210,29 +1276,27 @@ impl<'a> DisplayListFlattener<'a> {
     }
 
     fn add_prim_to_draw_list<P>(
         &mut self,
         info: &LayoutPrimitiveInfo,
         clip_chain_id: ClipChainId,
         clip_and_scroll: ScrollNodeAndClipChain,
         prim: P,
-        reference_frame_relative_offset: LayoutVector2D,
     )
     where
         P: Internable<InternData = PrimitiveSceneData>,
         P::Source: AsInstanceKind<Handle<P::Marker>> + InternDebug,
         Interners: InternerMut<P>,
     {
         let prim_instance = self.create_primitive(
             info,
             clip_chain_id,
             clip_and_scroll.spatial_node_index,
             prim,
-            reference_frame_relative_offset,
         );
         self.register_chase_primitive_by_rect(
             &info.rect,
             &prim_instance,
         );
         self.add_primitive_to_hit_testing_list(info, clip_and_scroll);
         self.add_primitive_to_draw_list(prim_instance);
     }
@@ -1822,17 +1886,16 @@ impl<'a> DisplayListFlattener<'a> {
         self.pending_shadow_items.push_back(ShadowItem::Shadow(PendingShadow {
             shadow,
             clip_and_scroll,
         }));
     }
 
     pub fn pop_all_shadows(
         &mut self,
-        reference_frame_relative_offset: LayoutVector2D,
     ) {
         assert!(!self.pending_shadow_items.is_empty(), "popped shadows, but none were present");
 
         let pipeline_id = self.sc_stack.last().unwrap().pipeline_id;
         let max_clip = LayoutRect::max_rect();
         let mut items = mem::replace(&mut self.pending_shadow_items, VecDeque::new());
 
         //
@@ -1876,49 +1939,44 @@ impl<'a> DisplayListFlattener<'a> {
 
                     for item in &items {
                         match item {
                             ShadowItem::Image(ref pending_image) => {
                                 self.add_shadow_prim(
                                     &pending_shadow,
                                     pending_image,
                                     &mut prims,
-                                    reference_frame_relative_offset,
                                 )
                             }
                             ShadowItem::LineDecoration(ref pending_line_dec) => {
                                 self.add_shadow_prim(
                                     &pending_shadow,
                                     pending_line_dec,
                                     &mut prims,
-                                    reference_frame_relative_offset,
                                 )
                             }
                             ShadowItem::NormalBorder(ref pending_border) => {
                                 self.add_shadow_prim(
                                     &pending_shadow,
                                     pending_border,
                                     &mut prims,
-                                    reference_frame_relative_offset,
                                 )
                             }
                             ShadowItem::Primitive(ref pending_primitive) => {
                                 self.add_shadow_prim(
                                     &pending_shadow,
                                     pending_primitive,
                                     &mut prims,
-                                    reference_frame_relative_offset,
                                 )
                             }
                             ShadowItem::TextRun(ref pending_text_run) => {
                                 self.add_shadow_prim(
                                     &pending_shadow,
                                     pending_text_run,
                                     &mut prims,
-                                    reference_frame_relative_offset,
                                 )
                             }
                             _ => {}
                         }
                     }
 
                     // No point in adding a shadow here if there were no primitives
                     // added to the shadow.
@@ -1988,56 +2046,50 @@ impl<'a> DisplayListFlattener<'a> {
                         // Add the shadow primitive. This must be done before pushing this
                         // picture on to the shadow stack, to avoid infinite recursion!
                         self.add_primitive_to_draw_list(shadow_prim_instance);
                     }
                 }
                 ShadowItem::Image(pending_image) => {
                     self.add_shadow_prim_to_draw_list(
                         pending_image,
-                        reference_frame_relative_offset,
                     )
                 },
                 ShadowItem::LineDecoration(pending_line_dec) => {
                     self.add_shadow_prim_to_draw_list(
                         pending_line_dec,
-                        reference_frame_relative_offset,
                     )
                 },
                 ShadowItem::NormalBorder(pending_border) => {
                     self.add_shadow_prim_to_draw_list(
                         pending_border,
-                        reference_frame_relative_offset,
                     )
                 },
                 ShadowItem::Primitive(pending_primitive) => {
                     self.add_shadow_prim_to_draw_list(
                         pending_primitive,
-                        reference_frame_relative_offset,
                     )
                 },
                 ShadowItem::TextRun(pending_text_run) => {
                     self.add_shadow_prim_to_draw_list(
                         pending_text_run,
-                        reference_frame_relative_offset,
                     )
                 },
             }
         }
 
         debug_assert!(items.is_empty());
         self.pending_shadow_items = items;
     }
 
     fn add_shadow_prim<P>(
         &mut self,
         pending_shadow: &PendingShadow,
         pending_primitive: &PendingPrimitive<P>,
         prims: &mut Vec<PrimitiveInstance>,
-        reference_frame_relative_offset: LayoutVector2D,
     )
     where
         P: Internable<InternData=PrimitiveSceneData> + CreateShadow,
         P::Source: AsInstanceKind<Handle<P::Marker>> + InternDebug,
         Interners: InternerMut<P>,
     {
         // Offset the local rect and clip rect by the shadow offset.
         let mut info = pending_primitive.info.clone();
@@ -2047,41 +2099,38 @@ impl<'a> DisplayListFlattener<'a> {
         // Construct and add a primitive for the given shadow.
         let shadow_prim_instance = self.create_primitive(
             &info,
             pending_primitive.clip_and_scroll.clip_chain_id,
             pending_primitive.clip_and_scroll.spatial_node_index,
             pending_primitive.prim.create_shadow(
                 &pending_shadow.shadow,
             ),
-            reference_frame_relative_offset,
         );
 
         // Add the new primitive to the shadow picture.
         prims.push(shadow_prim_instance);
     }
 
     fn add_shadow_prim_to_draw_list<P>(
         &mut self,
         pending_primitive: PendingPrimitive<P>,
-        reference_frame_relative_offset: LayoutVector2D,
     ) where
         P: Internable<InternData = PrimitiveSceneData> + IsVisible,
         P::Source: AsInstanceKind<Handle<P::Marker>> + InternDebug,
         Interners: InternerMut<P>,
     {
         // For a normal primitive, if it has alpha > 0, then we add this
         // as a normal primitive to the parent picture.
         if pending_primitive.prim.is_visible() {
             self.add_prim_to_draw_list(
                 &pending_primitive.info,
                 pending_primitive.clip_and_scroll.clip_chain_id,
                 pending_primitive.clip_and_scroll,
                 pending_primitive.prim,
-                reference_frame_relative_offset,
             );
         }
     }
 
     #[cfg(debug_assertions)]
     fn register_chase_primitive_by_rect(
         &mut self,
         rect: &LayoutRect,
@@ -2101,60 +2150,55 @@ impl<'a> DisplayListFlattener<'a> {
     ) {
     }
 
     pub fn add_solid_rectangle(
         &mut self,
         clip_and_scroll: ScrollNodeAndClipChain,
         info: &LayoutPrimitiveInfo,
         color: ColorF,
-        reference_frame_relative_offset: LayoutVector2D,
     ) {
         if color.a == 0.0 {
             // Don't add transparent rectangles to the draw list, but do consider them for hit
             // testing. This allows specifying invisible hit testing areas.
             self.add_primitive_to_hit_testing_list(info, clip_and_scroll);
             return;
         }
 
         self.add_primitive(
             clip_and_scroll,
             info,
             Vec::new(),
             PrimitiveKeyKind::Rectangle {
                 color: color.into(),
             },
-            reference_frame_relative_offset,
         );
     }
 
     pub fn add_clear_rectangle(
         &mut self,
         clip_and_scroll: ScrollNodeAndClipChain,
         info: &LayoutPrimitiveInfo,
-        reference_frame_relative_offset: LayoutVector2D,
     ) {
         self.add_primitive(
             clip_and_scroll,
             info,
             Vec::new(),
             PrimitiveKeyKind::Clear,
-            reference_frame_relative_offset,
         );
     }
 
     pub fn add_line(
         &mut self,
         clip_and_scroll: ScrollNodeAndClipChain,
         info: &LayoutPrimitiveInfo,
         wavy_line_thickness: f32,
         orientation: LineOrientation,
         color: ColorF,
         style: LineStyle,
-        reference_frame_relative_offset: LayoutVector2D,
     ) {
         // For line decorations, we can construct the render task cache key
         // here during scene building, since it doesn't depend on device
         // pixel ratio or transform.
         let mut info = info.clone();
 
         let size = get_line_decoration_sizes(
             &info.rect.size,
@@ -2206,28 +2250,26 @@ impl<'a> DisplayListFlattener<'a> {
         self.add_primitive(
             clip_and_scroll,
             &info,
             Vec::new(),
             LineDecoration {
                 cache_key,
                 color: color.into(),
             },
-            reference_frame_relative_offset,
         );
     }
 
     pub fn add_border(
         &mut self,
         clip_and_scroll: ScrollNodeAndClipChain,
         info: &LayoutPrimitiveInfo,
         border_item: &BorderDisplayItem,
         gradient_stops: ItemRange<GradientStop>,
         pipeline_id: PipelineId,
-        reference_frame_relative_offset: LayoutVector2D,
     ) {
         match border_item.details {
             BorderDetails::NinePatch(ref border) => {
                 let nine_patch = NinePatchDescriptor {
                     width: border.width,
                     height: border.height,
                     slice: border.slice,
                     fill: border.fill,
@@ -2248,17 +2290,16 @@ impl<'a> DisplayListFlattener<'a> {
                             nine_patch,
                         };
 
                         self.add_nonshadowable_primitive(
                             clip_and_scroll,
                             info,
                             Vec::new(),
                             prim,
-                            reference_frame_relative_offset,
                         );
                     }
                     NinePatchBorderSource::Gradient(gradient) => {
                         let prim = match self.create_linear_gradient_prim(
                             &info,
                             gradient.start_point,
                             gradient.end_point,
                             gradient_stops,
@@ -2272,17 +2313,16 @@ impl<'a> DisplayListFlattener<'a> {
                             None => return,
                         };
 
                         self.add_nonshadowable_primitive(
                             clip_and_scroll,
                             info,
                             Vec::new(),
                             prim,
-                            reference_frame_relative_offset,
                         );
                     }
                     NinePatchBorderSource::RadialGradient(gradient) => {
                         let prim = self.create_radial_gradient_prim(
                             &info,
                             gradient.center,
                             gradient.start_offset * gradient.radius.width,
                             gradient.end_offset * gradient.radius.width,
@@ -2295,28 +2335,26 @@ impl<'a> DisplayListFlattener<'a> {
                             Some(Box::new(nine_patch)),
                         );
 
                         self.add_nonshadowable_primitive(
                             clip_and_scroll,
                             info,
                             Vec::new(),
                             prim,
-                            reference_frame_relative_offset,
                         );
                     }
                 };
             }
             BorderDetails::Normal(ref border) => {
                 self.add_normal_border(
                     info,
                     border,
                     border_item.widths,
                     clip_and_scroll,
-                    reference_frame_relative_offset,
                 );
             }
         }
     }
 
     pub fn create_linear_gradient_prim(
         &mut self,
         info: &LayoutPrimitiveInfo,
@@ -2426,24 +2464,25 @@ impl<'a> DisplayListFlattener<'a> {
             nine_patch,
             stops,
         }
     }
 
     pub fn add_text(
         &mut self,
         clip_and_scroll: ScrollNodeAndClipChain,
-        offset: LayoutVector2D,
         prim_info: &LayoutPrimitiveInfo,
         font_instance_key: &FontInstanceKey,
         text_color: &ColorF,
         glyph_range: ItemRange<GlyphInstance>,
         glyph_options: Option<GlyphOptions>,
         pipeline_id: PipelineId,
     ) {
+        let offset = self.rf_mapper.current_offset();
+
         let text_run = {
             let instance_map = self.font_instances.read().unwrap();
             let font_instance = match instance_map.get(font_instance_key) {
                 Some(instance) => instance,
                 None => {
                     warn!("Unknown font instance key");
                     debug!("key={:?}", font_instance_key);
                     return;
@@ -2499,32 +2538,30 @@ impl<'a> DisplayListFlattener<'a> {
             }
         };
 
         self.add_primitive(
             clip_and_scroll,
             prim_info,
             Vec::new(),
             text_run,
-            offset,
         );
     }
 
     pub fn add_image(
         &mut self,
         clip_and_scroll: ScrollNodeAndClipChain,
         info: &LayoutPrimitiveInfo,
         stretch_size: LayoutSize,
         mut tile_spacing: LayoutSize,
         sub_rect: Option<TexelRect>,
         image_key: ImageKey,
         image_rendering: ImageRendering,
         alpha_type: AlphaType,
         color: ColorF,
-        reference_frame_relative_offset: LayoutVector2D,
     ) {
         let mut prim_rect = info.rect;
         simplify_repeated_primitive(&stretch_size, &mut tile_spacing, &mut prim_rect);
         let info = LayoutPrimitiveInfo {
             rect: prim_rect,
             .. *info
         };
 
@@ -2549,29 +2586,27 @@ impl<'a> DisplayListFlattener<'a> {
                 key: image_key,
                 tile_spacing: tile_spacing.into(),
                 stretch_size: stretch_size.into(),
                 color: color.into(),
                 sub_rect,
                 image_rendering,
                 alpha_type,
             },
-            reference_frame_relative_offset,
         );
     }
 
     pub fn add_yuv_image(
         &mut self,
         clip_and_scroll: ScrollNodeAndClipChain,
         info: &LayoutPrimitiveInfo,
         yuv_data: YuvData,
         color_depth: ColorDepth,
         color_space: YuvColorSpace,
         image_rendering: ImageRendering,
-        reference_frame_relative_offset: LayoutVector2D,
     ) {
         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],
         };
 
@@ -2581,17 +2616,16 @@ impl<'a> DisplayListFlattener<'a> {
             Vec::new(),
             YuvImage {
                 color_depth,
                 yuv_key,
                 format,
                 color_space,
                 image_rendering,
             },
-            reference_frame_relative_offset,
         );
     }
 
     pub fn add_primitive_instance_to_3d_root(&mut self, instance: PrimitiveInstance) {
         // find the 3D root and append to the children list
         for sc in self.sc_stack.iter_mut().rev() {
             match sc.context_3d {
                 Picture3DContext::In { root_data: Some(ref mut prims), .. } => {