Bug 1523210 - Fix text runs being interned due to scroll offsets. r=jrmuizel
authorGlenn Watson <github@intuitionlibrary.com>
Mon, 28 Jan 2019 05:18:19 +0000
changeset 515598 f86078e28b8726670017f74e6f0e5426768b63e5
parent 515597 32b3bc02aaefacf53c2f843b5a7dd9de852dff87
child 515599 ab7f23ee7562fd540b9b4705a90cb7aa5aa9b8a1
push id1953
push userffxbld-merge
push dateMon, 11 Mar 2019 12:10:20 +0000
treeherdermozilla-release@9c35dcbaa899 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersjrmuizel
bugs1523210
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 1523210 - Fix text runs being interned due to scroll offsets. r=jrmuizel Differential Revision: https://phabricator.services.mozilla.com/D17792
gfx/wr/webrender/res/ps_text_run.glsl
gfx/wr/webrender/src/batch.rs
gfx/wr/webrender/src/border.rs
gfx/wr/webrender/src/box_shadow.rs
gfx/wr/webrender/src/display_list_flattener.rs
gfx/wr/webrender/src/prim_store/borders.rs
gfx/wr/webrender/src/prim_store/gradient.rs
gfx/wr/webrender/src/prim_store/image.rs
gfx/wr/webrender/src/prim_store/line_dec.rs
gfx/wr/webrender/src/prim_store/mod.rs
gfx/wr/webrender/src/prim_store/picture.rs
gfx/wr/webrender/src/prim_store/text_run.rs
--- a/gfx/wr/webrender/res/ps_text_run.glsl
+++ b/gfx/wr/webrender/res/ps_text_run.glsl
@@ -10,17 +10,17 @@ flat varying vec4 vUvBorder;
 flat varying vec2 vMaskSwizzle;
 
 #ifdef WR_FEATURE_GLYPH_TRANSFORM
 varying vec4 vUvClip;
 #endif
 
 #ifdef WR_VERTEX_SHADER
 
-#define VECS_PER_TEXT_RUN           3
+#define VECS_PER_TEXT_RUN           2
 #define GLYPHS_PER_GPU_BLOCK        2U
 
 struct Glyph {
     vec2 offset;
 };
 
 Glyph fetch_glyph(int specific_prim_address,
                   int glyph_index) {
@@ -48,22 +48,21 @@ struct GlyphResource {
 GlyphResource fetch_glyph_resource(int address) {
     vec4 data[2] = fetch_from_gpu_cache_2(address);
     return GlyphResource(data[0], data[1].x, data[1].yz, data[1].w);
 }
 
 struct TextRun {
     vec4 color;
     vec4 bg_color;
-    vec2 offset;
 };
 
 TextRun fetch_text_run(int address) {
-    vec4 data[3] = fetch_from_gpu_cache_3(address);
-    return TextRun(data[0], data[1], data[2].xy);
+    vec4 data[2] = fetch_from_gpu_cache_2(address);
+    return TextRun(data[0], data[1]);
 }
 
 VertexInfo write_text_vertex(RectWithSize local_clip_rect,
                              float z,
                              Transform transform,
                              PictureTask task,
                              vec2 text_offset,
                              vec2 glyph_offset,
@@ -155,24 +154,24 @@ void main(void) {
 
     PrimitiveHeader ph = fetch_prim_header(prim_header_address);
 
     Transform transform = fetch_transform(ph.transform_id);
     ClipArea clip_area = fetch_clip_area(ph.clip_task_index);
     PictureTask task = fetch_picture_task(ph.render_task_index);
 
     TextRun text = fetch_text_run(ph.specific_prim_address);
-    vec2 text_offset = ph.local_rect.p0 - text.offset;
+    vec2 text_offset = vec2(ph.user_data.xy) / 256.0;
 
     if (color_mode == COLOR_MODE_FROM_PASS) {
         color_mode = uMode;
     }
 
     Glyph glyph = fetch_glyph(ph.specific_prim_address, glyph_index);
-    glyph.offset += text.offset;
+    glyph.offset += ph.local_rect.p0 - text_offset;
 
     GlyphResource res = fetch_glyph_resource(resource_address);
 
 #ifdef WR_FEATURE_GLYPH_TRANSFORM
     // Transform from local space to glyph space.
     mat2 glyph_transform = mat2(transform.m) * task.common_data.device_pixel_scale;
 
     // Compute the glyph rect in glyph space.
--- a/gfx/wr/webrender/src/batch.rs
+++ b/gfx/wr/webrender/src/batch.rs
@@ -831,17 +831,25 @@ impl AlphaBatchBuilder {
                             GlyphFormat::ColorBitmap => {
                                 (
                                     BlendMode::PremultipliedAlpha,
                                     ShaderColorMode::ColorBitmap,
                                 )
                             }
                         };
 
-                        let prim_header_index = prim_headers.push(&prim_header, z_id, [0; 3]);
+                        let prim_header_index = prim_headers.push(
+                            &prim_header,
+                            z_id,
+                            [
+                                (run.reference_frame_relative_offset.x * 256.0) as i32,
+                                (run.reference_frame_relative_offset.y * 256.0) as i32,
+                                0,
+                            ],
+                        );
                         let key = BatchKey::new(kind, blend_mode, textures);
                         let base_instance = GlyphInstance::new(
                             prim_header_index,
                         );
                         let batch = alpha_batch_list.set_params_and_get_batch(
                             key,
                             bounding_rect,
                             z_id,
--- 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, RepeatMode, TexelRect};
+use api::{AuHelpers, LayoutPoint, RepeatMode, TexelRect, LayoutVector2D};
 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;
@@ -207,28 +207,30 @@ 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,16 +72,17 @@ 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),
@@ -161,16 +162,17 @@ 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.
@@ -250,16 +252,17 @@ 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
@@ -715,26 +715,28 @@ impl<'a> DisplayListFlattener<'a> {
                     &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,
@@ -744,32 +746,35 @@ impl<'a> DisplayListFlattener<'a> {
                     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(),
@@ -779,16 +784,17 @@ 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,
@@ -801,16 +807,17 @@ 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 bounds = box_shadow_info
                     .box_bounds
                     .translate(&reference_frame_relative_offset);
                 let mut prim_info = prim_info.clone();
                 prim_info.rect = bounds;
@@ -818,25 +825,27 @@ impl<'a> DisplayListFlattener<'a> {
                     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,
@@ -971,17 +980,17 @@ impl<'a> DisplayListFlattener<'a> {
             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();
+                self.pop_all_shadows(reference_frame_relative_offset);
             }
             SpecificDisplayItem::PushCacheMarker(_marker) => {
             }
             SpecificDisplayItem::PopCacheMarker => {
             }
         }
         None
     }
@@ -1026,16 +1035,17 @@ 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);
@@ -1045,18 +1055,21 @@ impl<'a> DisplayListFlattener<'a> {
             interner
             .intern(&prim_key, || {
                 PrimitiveSceneData {
                     prim_size: info.rect.size,
                     is_backface_visible: info.is_backface_visible,
                 }
             });
 
-        let instance_kind = prim_key.as_instance_kind(prim_data_handle,
-                                                      &mut self.prim_store);
+        let instance_kind = prim_key.as_instance_kind(
+            prim_data_handle,
+            &mut self.prim_store,
+            reference_frame_relative_offset,
+        );
 
         PrimitiveInstance::new(
             info.rect.origin,
             info.clip_rect,
             instance_kind,
             clip_chain_id,
             spatial_node_index,
         )
@@ -1101,54 +1114,63 @@ 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(
                 clip_items,
                 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
+                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);
+            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,
                 info: *info,
@@ -1158,27 +1180,29 @@ 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);
     }
@@ -1742,17 +1766,20 @@ impl<'a> DisplayListFlattener<'a> {
         // Store this shadow in the pending list, for processing
         // during pop_all_shadows.
         self.pending_shadow_items.push_back(ShadowItem::Shadow(PendingShadow {
             shadow,
             clip_and_scroll,
         }));
     }
 
-    pub fn pop_all_shadows(&mut self) {
+    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());
 
         //
         // The pending_shadow_items queue contains a list of shadows and primitives
@@ -1791,29 +1818,54 @@ impl<'a> DisplayListFlattener<'a> {
 
                     // Add any primitives that come after this shadow in the item
                     // list to this shadow.
                     let mut prims = Vec::new();
 
                     for item in &items {
                         match item {
                             ShadowItem::Image(ref pending_image) => {
-                                self.add_shadow_prim(&pending_shadow, pending_image, &mut prims)
+                                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)
+                                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)
+                                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)
+                                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)
+                                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.
                     if !prims.is_empty() {
@@ -1880,42 +1932,58 @@ 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)
+                    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)
+                    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)
+                    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)
+                    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)
+                    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();
@@ -1925,36 +1993,41 @@ 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>)
-    where
+    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,
@@ -1974,55 +2047,60 @@ 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,
@@ -2074,26 +2152,28 @@ 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,
@@ -2114,16 +2194,17 @@ 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,
@@ -2137,16 +2218,17 @@ 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,
@@ -2159,26 +2241,28 @@ 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,
@@ -2357,40 +2441,41 @@ impl<'a> DisplayListFlattener<'a> {
                         point: glyph.point - prim_offset,
                     }
                 })
                 .collect();
 
             TextRun {
                 glyphs: Arc::new(glyphs),
                 font,
-                offset: prim_offset,
                 shadow: false,
             }
         };
 
         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
         };
 
@@ -2415,27 +2500,29 @@ 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],
         };
 
@@ -2445,16 +2532,17 @@ 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), .. } => {
@@ -2468,16 +2556,17 @@ impl<'a> DisplayListFlattener<'a> {
     }
 }
 
 pub trait AsInstanceKind<H> {
     fn as_instance_kind(
         &self,
         data_handle: H,
         prim_store: &mut PrimitiveStore,
+        reference_frame_relative_offset: LayoutVector2D,
     ) -> PrimitiveInstanceKind;
 }
 
 pub trait CreateShadow {
     fn create_shadow(&self, shadow: &Shadow) -> Self;
 }
 
 pub trait IsVisible {
--- a/gfx/wr/webrender/src/prim_store/borders.rs
+++ b/gfx/wr/webrender/src/prim_store/borders.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::{
     AuHelpers, LayoutPrimitiveInfo, LayoutSideOffsets,
     LayoutSideOffsetsAu, LayoutSize, NormalBorder, PremultipliedColorF,
-    Shadow
+    Shadow, LayoutVector2D,
 };
 use border::create_border_segments;
 use border::NormalBorderAu;
 use display_list_flattener::{AsInstanceKind, CreateShadow, IsVisible};
 use frame_builder::{FrameBuildingState};
 use gpu_cache::GpuDataRequest;
 use intern;
 use intern_types;
@@ -51,16 +51,17 @@ impl intern::InternDebug for NormalBorde
 
 impl AsInstanceKind<NormalBorderDataHandle> for NormalBorderKey {
     /// Construct a primitive instance that matches the type
     /// of primitive key.
     fn as_instance_kind(
         &self,
         data_handle: NormalBorderDataHandle,
         _: &mut PrimitiveStore,
+        _reference_frame_relative_offset: LayoutVector2D,
     ) -> PrimitiveInstanceKind {
         PrimitiveInstanceKind::NormalBorder {
             data_handle,
             cache_handles: storage::Range::empty(),
         }
     }
 }
 
@@ -226,16 +227,17 @@ impl intern::InternDebug for ImageBorder
 
 impl AsInstanceKind<ImageBorderDataHandle> for ImageBorderKey {
     /// Construct a primitive instance that matches the type
     /// of primitive key.
     fn as_instance_kind(
         &self,
         data_handle: ImageBorderDataHandle,
         _: &mut PrimitiveStore,
+        _reference_frame_relative_offset: LayoutVector2D,
     ) -> PrimitiveInstanceKind {
         PrimitiveInstanceKind::ImageBorder {
             data_handle
         }
     }
 }
 
 #[cfg_attr(feature = "capture", derive(Serialize))]
--- a/gfx/wr/webrender/src/prim_store/gradient.rs
+++ b/gfx/wr/webrender/src/prim_store/gradient.rs
@@ -1,15 +1,15 @@
 /* This Source Code Form is subject to the terms of the Mozilla Public
  * License, v. 2.0. If a copy of the MPL was not distributed with this
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 use api::{
     ColorF, ColorU,ExtendMode, GradientStop, LayoutPoint, LayoutSize,
-    LayoutPrimitiveInfo, PremultipliedColorF
+    LayoutPrimitiveInfo, PremultipliedColorF, LayoutVector2D,
 };
 use display_list_flattener::{AsInstanceKind, IsVisible};
 use frame_builder::FrameBuildingState;
 use gpu_cache::{GpuCacheHandle, GpuDataRequest};
 use intern::{Internable, InternDebug};
 use intern_types;
 use prim_store::{BrushSegment, GradientTileRange};
 use prim_store::{PrimitiveInstanceKind, PrimitiveOpacity, PrimitiveSceneData};
@@ -79,16 +79,17 @@ impl InternDebug for LinearGradientKey {
 
 impl AsInstanceKind<LinearGradientDataHandle> for LinearGradientKey {
     /// Construct a primitive instance that matches the type
     /// of primitive key.
     fn as_instance_kind(
         &self,
         data_handle: LinearGradientDataHandle,
         _prim_store: &mut PrimitiveStore,
+        _reference_frame_relative_offset: LayoutVector2D,
     ) -> PrimitiveInstanceKind {
         PrimitiveInstanceKind::LinearGradient {
             data_handle,
             visible_tiles_range: GradientTileRange::empty(),
         }
     }
 }
 
@@ -327,16 +328,17 @@ impl InternDebug for RadialGradientKey {
 
 impl AsInstanceKind<RadialGradientDataHandle> for RadialGradientKey {
     /// Construct a primitive instance that matches the type
     /// of primitive key.
     fn as_instance_kind(
         &self,
         data_handle: RadialGradientDataHandle,
         _prim_store: &mut PrimitiveStore,
+        _reference_frame_relative_offset: LayoutVector2D,
     ) -> PrimitiveInstanceKind {
         PrimitiveInstanceKind::RadialGradient {
             data_handle,
             visible_tiles_range: GradientTileRange::empty(),
         }
     }
 }
 
--- a/gfx/wr/webrender/src/prim_store/image.rs
+++ b/gfx/wr/webrender/src/prim_store/image.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::{
     AlphaType, ColorDepth, ColorF, ColorU, DeviceIntRect, DeviceIntSideOffsets,
     DeviceIntSize, ImageRendering, LayoutRect, LayoutSize, LayoutPrimitiveInfo,
-    PremultipliedColorF, Shadow, TileOffset, YuvColorSpace, YuvFormat
+    PremultipliedColorF, Shadow, TileOffset, YuvColorSpace, YuvFormat, LayoutVector2D,
 };
 use api::ImageKey as ApiImageKey;
 use display_list_flattener::{AsInstanceKind, CreateShadow, IsVisible};
 use frame_builder::FrameBuildingState;
 use gpu_cache::{GpuCacheHandle, GpuDataRequest};
 use intern::{Internable, InternDebug};
 use intern_types;
 use prim_store::{
@@ -102,16 +102,17 @@ impl InternDebug for ImageKey {}
 
 impl AsInstanceKind<ImageDataHandle> for ImageKey {
     /// Construct a primitive instance that matches the type
     /// of primitive key.
     fn as_instance_kind(
         &self,
         data_handle: ImageDataHandle,
         prim_store: &mut PrimitiveStore,
+        _reference_frame_relative_offset: LayoutVector2D,
     ) -> PrimitiveInstanceKind {
         // TODO(gw): Refactor this to not need a separate image
         //           instance (see ImageInstance struct).
         let image_instance_index = prim_store.images.push(ImageInstance {
             opacity_binding_index: OpacityBindingIndex::INVALID,
             segment_instance_index: SegmentInstanceIndex::INVALID,
             visible_tiles: Vec::new(),
         });
@@ -411,16 +412,17 @@ impl InternDebug for YuvImageKey {}
 
 impl AsInstanceKind<YuvImageDataHandle> for YuvImageKey {
     /// Construct a primitive instance that matches the type
     /// of primitive key.
     fn as_instance_kind(
         &self,
         data_handle: YuvImageDataHandle,
         _prim_store: &mut PrimitiveStore,
+        _reference_frame_relative_offset: LayoutVector2D,
     ) -> PrimitiveInstanceKind {
         PrimitiveInstanceKind::YuvImage {
             data_handle,
             segment_instance_index: SegmentInstanceIndex::INVALID
         }
     }
 }
 
--- a/gfx/wr/webrender/src/prim_store/line_dec.rs
+++ b/gfx/wr/webrender/src/prim_store/line_dec.rs
@@ -1,14 +1,14 @@
 /* This Source Code Form is subject to the terms of the Mozilla Public
  * License, v. 2.0. If a copy of the MPL was not distributed with this
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 use api::{
-    ColorF, ColorU, LayoutPrimitiveInfo, LayoutSizeAu,
+    ColorF, ColorU, LayoutPrimitiveInfo, LayoutSizeAu, LayoutVector2D,
     LineOrientation, LineStyle, PremultipliedColorF, Shadow,
 };
 use app_units::Au;
 use display_list_flattener::{AsInstanceKind, CreateShadow, IsVisible};
 use frame_builder::{FrameBuildingState};
 use gpu_cache::GpuDataRequest;
 use intern;
 use intern_types;
@@ -61,16 +61,17 @@ impl intern::InternDebug for LineDecorat
 
 impl AsInstanceKind<LineDecorationDataHandle> for LineDecorationKey {
     /// Construct a primitive instance that matches the type
     /// of primitive key.
     fn as_instance_kind(
         &self,
         data_handle: LineDecorationDataHandle,
         _: &mut PrimitiveStore,
+        _reference_frame_relative_offset: LayoutVector2D,
     ) -> PrimitiveInstanceKind {
         PrimitiveInstanceKind::LineDecoration {
             data_handle,
             cache_handle: None,
         }
     }
 }
 
--- a/gfx/wr/webrender/src/prim_store/mod.rs
+++ b/gfx/wr/webrender/src/prim_store/mod.rs
@@ -660,16 +660,17 @@ impl intern::InternDebug for PrimitiveKe
 
 impl AsInstanceKind<PrimitiveDataHandle> for PrimitiveKey {
     /// Construct a primitive instance that matches the type
     /// of primitive key.
     fn as_instance_kind(
         &self,
         data_handle: PrimitiveDataHandle,
         _: &mut PrimitiveStore,
+        _reference_frame_relative_offset: LayoutVector2D,
     ) -> PrimitiveInstanceKind {
         match self.kind {
             PrimitiveKeyKind::Clear => {
                 PrimitiveInstanceKind::Clear {
                     data_handle
                 }
             }
             PrimitiveKeyKind::Rectangle { .. } => {
@@ -2502,31 +2503,32 @@ impl PrimitiveStore {
                             );
                             render_tasks.add(task)
                         }
                     ));
                 }
             }
             PrimitiveInstanceKind::TextRun { data_handle, run_index, .. } => {
                 let prim_data = &mut data_stores.text_run[*data_handle];
+                let run = &mut self.text_runs[*run_index];
 
                 // Update the template this instane references, which may refresh the GPU
                 // cache with any shared template data.
                 prim_data.update(frame_state);
 
                 // The transform only makes sense for screen space rasterization
                 let transform = prim_context.spatial_node.world_content_transform.to_transform();
+                let prim_offset = prim_instance.prim_origin.to_vector() - run.reference_frame_relative_offset;
 
                 // TODO(gw): This match is a bit untidy, but it should disappear completely
                 //           once the prepare_prims and batching are unified. When that
                 //           happens, we can use the cache handle immediately, and not need
                 //           to temporarily store it in the primitive instance.
-                let run = &mut self.text_runs[*run_index];
                 run.prepare_for_render(
-                    prim_data.offset,
+                    prim_offset,
                     &prim_data.font,
                     &prim_data.glyphs,
                     frame_context.device_pixel_scale,
                     &transform,
                     pic_context,
                     frame_state.resource_cache,
                     frame_state.gpu_cache,
                     frame_state.render_tasks,
--- a/gfx/wr/webrender/src/prim_store/picture.rs
+++ b/gfx/wr/webrender/src/prim_store/picture.rs
@@ -1,15 +1,15 @@
 /* This Source Code Form is subject to the terms of the Mozilla Public
  * License, v. 2.0. If a copy of the MPL was not distributed with this
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 use api::{
     ColorU, FilterOp, LayoutSize, LayoutPrimitiveInfo, MixBlendMode,
-    PropertyBinding, PropertyBindingId,
+    PropertyBinding, PropertyBindingId, LayoutVector2D,
 };
 use app_units::Au;
 use display_list_flattener::{AsInstanceKind, IsVisible};
 use intern::{Internable, InternDebug};
 use intern_types;
 use picture::PictureCompositeMode;
 use prim_store::{
     PrimKey, PrimKeyCommonData, PrimTemplate, PrimTemplateCommonData,
@@ -156,16 +156,17 @@ impl InternDebug for PictureKey {}
 
 impl AsInstanceKind<PictureDataHandle> for PictureKey {
     /// Construct a primitive instance that matches the type
     /// of primitive key.
     fn as_instance_kind(
         &self,
         _: PictureDataHandle,
         _: &mut PrimitiveStore,
+        _reference_frame_relative_offset: LayoutVector2D,
     ) -> PrimitiveInstanceKind {
         // Should never be hit as this method should not be
         // called for pictures.
         unreachable!();
     }
 }
 
 #[cfg_attr(feature = "capture", derive(Serialize))]
--- a/gfx/wr/webrender/src/prim_store/text_run.rs
+++ b/gfx/wr/webrender/src/prim_store/text_run.rs
@@ -7,17 +7,17 @@ use api::{LayoutToWorldTransform, Raster
 use api::{LayoutVector2D, Shadow};
 use display_list_flattener::{AsInstanceKind, CreateShadow, IsVisible};
 use frame_builder::{FrameBuildingState, PictureContext};
 use glyph_rasterizer::{FontInstance, FontTransform, GlyphKey, FONT_SIZE_LIMIT};
 use gpu_cache::GpuCache;
 use intern;
 use intern_types;
 use prim_store::{PrimitiveOpacity, PrimitiveSceneData,  PrimitiveScratchBuffer};
-use prim_store::{PrimitiveStore, PrimKeyCommonData, PrimTemplateCommonData, VectorKey};
+use prim_store::{PrimitiveStore, PrimKeyCommonData, PrimTemplateCommonData};
 use render_task::{RenderTaskTree};
 use renderer::{MAX_VERTEX_TEXTURE_WIDTH};
 use resource_cache::{ResourceCache};
 use util::{MatrixHelpers};
 use prim_store::PrimitiveInstanceKind;
 use std::ops;
 use std::sync::Arc;
 use storage;
@@ -25,65 +25,64 @@ use util::PrimaryArc;
 
 /// A run of glyphs, with associated font information.
 #[cfg_attr(feature = "capture", derive(Serialize))]
 #[cfg_attr(feature = "replay", derive(Deserialize))]
 #[derive(Debug, Clone, Eq, MallocSizeOf, PartialEq, Hash)]
 pub struct TextRunKey {
     pub common: PrimKeyCommonData,
     pub font: FontInstance,
-    pub offset: VectorKey,
     pub glyphs: PrimaryArc<Vec<GlyphInstance>>,
     pub shadow: bool,
 }
 
 impl TextRunKey {
     pub fn new(
         info: &LayoutPrimitiveInfo,
         text_run: TextRun,
     ) -> Self {
         TextRunKey {
             common: PrimKeyCommonData::with_info(
                 info,
             ),
             font: text_run.font,
-            offset: text_run.offset.into(),
             glyphs: PrimaryArc(text_run.glyphs),
             shadow: text_run.shadow,
         }
     }
 }
 
 impl intern::InternDebug for TextRunKey {}
 
 impl AsInstanceKind<TextRunDataHandle> for TextRunKey {
     /// Construct a primitive instance that matches the type
     /// of primitive key.
     fn as_instance_kind(
         &self,
         data_handle: TextRunDataHandle,
         prim_store: &mut PrimitiveStore,
+        reference_frame_relative_offset: LayoutVector2D,
     ) -> PrimitiveInstanceKind {
         let run_index = prim_store.text_runs.push(TextRunPrimitive {
             used_font: self.font.clone(),
             glyph_keys_range: storage::Range::empty(),
             shadow: self.shadow,
+            reference_frame_relative_offset,
         });
 
         PrimitiveInstanceKind::TextRun{ data_handle, run_index }
     }
 }
 
 #[cfg_attr(feature = "capture", derive(Serialize))]
 #[cfg_attr(feature = "replay", derive(Deserialize))]
 #[derive(MallocSizeOf)]
 pub struct TextRunTemplate {
     pub common: PrimTemplateCommonData,
     pub font: FontInstance,
-    pub offset: LayoutVector2D,
     #[ignore_malloc_size_of = "Measured via PrimaryArc"]
     pub glyphs: Arc<Vec<GlyphInstance>>,
 }
 
 impl ops::Deref for TextRunTemplate {
     type Target = PrimTemplateCommonData;
     fn deref(&self) -> &Self::Target {
         &self.common
@@ -97,17 +96,16 @@ impl ops::DerefMut for TextRunTemplate {
 }
 
 impl From<TextRunKey> for TextRunTemplate {
     fn from(item: TextRunKey) -> Self {
         let common = PrimTemplateCommonData::with_key_common(item.common);
         TextRunTemplate {
             common,
             font: item.font,
-            offset: item.offset.into(),
             glyphs: item.glyphs.0,
         }
     }
 }
 
 impl TextRunTemplate {
     /// 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
@@ -125,22 +123,16 @@ impl TextRunTemplate {
         &mut self,
         frame_state: &mut FrameBuildingState,
     ) {
         if let Some(mut request) = frame_state.gpu_cache.request(&mut self.common.gpu_cache_handle) {
             request.push(ColorF::from(self.font.color).premultiplied());
             // this is the only case where we need to provide plain color to GPU
             let bg_color = ColorF::from(self.font.bg_color);
             request.push([bg_color.r, bg_color.g, bg_color.b, 1.0]);
-            request.push([
-                self.offset.x,
-                self.offset.y,
-                0.0,
-                0.0,
-            ]);
 
             let mut gpu_block = [0.0; 4];
             for (i, src) in self.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;
@@ -161,17 +153,16 @@ impl TextRunTemplate {
         }
     }
 }
 
 pub use intern_types::text_run::Handle as TextRunDataHandle;
 
 pub struct TextRun {
     pub font: FontInstance,
-    pub offset: LayoutVector2D,
     pub glyphs: Arc<Vec<GlyphInstance>>,
     pub shadow: bool,
 }
 
 impl intern::Internable for TextRun {
     type Marker = intern_types::text_run::Marker;
     type Source = TextRunKey;
     type StoreData = TextRunTemplate;
@@ -197,32 +188,32 @@ impl CreateShadow for TextRun {
         };
         if shadow.blur_radius > 0.0 {
             font.disable_subpixel_aa();
         }
 
         TextRun {
             font,
             glyphs: self.glyphs.clone(),
-            offset: self.offset + shadow.offset,
             shadow: true
         }
     }
 }
 
 impl IsVisible for TextRun {
     fn is_visible(&self) -> bool {
         self.font.color.a > 0
     }
 }
 
 #[derive(Debug)]
 pub struct TextRunPrimitive {
     pub used_font: FontInstance,
     pub glyph_keys_range: storage::Range<GlyphKey>,
+    pub reference_frame_relative_offset: LayoutVector2D,
     pub shadow: bool,
 }
 
 impl TextRunPrimitive {
     pub fn update_font_instance(
         &mut self,
         specified_font: &FontInstance,
         device_pixel_scale: DevicePixelScale,
@@ -327,13 +318,13 @@ impl TextRunPrimitive {
 fn test_struct_sizes() {
     use std::mem;
     // The sizes of these structures are critical for performance on a number of
     // talos stress tests. If you get a failure here on CI, there's two possibilities:
     // (a) You made a structure smaller than it currently is. Great work! Update the
     //     test expectations and move on.
     // (b) You made a structure larger. This is not necessarily a problem, but should only
     //     be done with care, and after checking if talos performance regresses badly.
-    assert_eq!(mem::size_of::<TextRun>(), 96, "TextRun size changed");
-    assert_eq!(mem::size_of::<TextRunTemplate>(), 112, "TextRunTemplate size changed");
-    assert_eq!(mem::size_of::<TextRunKey>(), 104, "TextRunKey size changed");
-    assert_eq!(mem::size_of::<TextRunPrimitive>(), 88, "TextRunPrimitive size changed");
+    assert_eq!(mem::size_of::<TextRun>(), 88, "TextRun size changed");
+    assert_eq!(mem::size_of::<TextRunTemplate>(), 104, "TextRunTemplate size changed");
+    assert_eq!(mem::size_of::<TextRunKey>(), 96, "TextRunKey size changed");
+    assert_eq!(mem::size_of::<TextRunPrimitive>(), 96, "TextRunPrimitive size changed");
 }