Bug 1467096 - Update webrender to commit dd30fbb21c876b252b805b607bd04f3bab1fd228. r?Gankro draft
authorKartikaya Gupta <kgupta@mozilla.com>
Fri, 15 Jun 2018 09:46:46 -0400
changeset 807717 cc4efc800f1bbf83bd698fd0e5b68c433c8c44b9
parent 807714 0b5495dc100dd3bfda0886a4ad563a3c729c9b72
push id113181
push userkgupta@mozilla.com
push dateFri, 15 Jun 2018 13:47:29 +0000
reviewersGankro
bugs1467096
milestone62.0a1
Bug 1467096 - Update webrender to commit dd30fbb21c876b252b805b607bd04f3bab1fd228. r?Gankro MozReview-Commit-ID: 43CVE7eDKM8
gfx/webrender/Cargo.toml
gfx/webrender/examples/alpha_perf.rs
gfx/webrender/examples/animation.rs
gfx/webrender/examples/basic.rs
gfx/webrender/examples/blob.rs
gfx/webrender/examples/common/boilerplate.rs
gfx/webrender/examples/common/image_helper.rs
gfx/webrender/examples/document.rs
gfx/webrender/examples/frame_output.rs
gfx/webrender/examples/iframe.rs
gfx/webrender/examples/image_resize.rs
gfx/webrender/examples/multiwindow.rs
gfx/webrender/examples/scrolling.rs
gfx/webrender/examples/texture_cache_stress.rs
gfx/webrender/examples/yuv.rs
gfx/webrender/res/ps_text_run.glsl
gfx/webrender/src/batch.rs
gfx/webrender/src/display_list_flattener.rs
gfx/webrender/src/glyph_rasterizer/mod.rs
gfx/webrender/src/glyph_rasterizer/pathfinder.rs
gfx/webrender/src/platform/macos/font.rs
gfx/webrender/src/platform/unix/font.rs
gfx/webrender/src/platform/windows/font.rs
gfx/webrender/src/prim_store.rs
gfx/webrender/src/render_backend.rs
gfx/webrender/src/renderer.rs
gfx/webrender/src/shade.rs
gfx/webrender/src/util.rs
gfx/webrender_bindings/revision.txt
gfx/wrench/src/args.yaml
gfx/wrench/src/main.rs
--- a/gfx/webrender/Cargo.toml
+++ b/gfx/webrender/Cargo.toml
@@ -58,20 +58,16 @@ git = "https://github.com/pcwalton/pathf
 optional = true
 
 [dependencies.pathfinder_path_utils]
 git = "https://github.com/pcwalton/pathfinder"
 optional = true
 
 [dev-dependencies]
 mozangle = "0.1"
-env_logger = "0.5"
-rand = "0.3"                # for the benchmarks
-glutin = "0.15"             # for the example apps
-winit = "0.13"              # for the example apps
 
 [target.'cfg(any(target_os = "android", all(unix, not(target_os = "macos"))))'.dependencies]
 freetype = { version = "0.4", default-features = false }
 
 [target.'cfg(target_os = "windows")'.dependencies]
 dwrote = "0.4.1"
 
 [target.'cfg(target_os = "macos")'.dependencies]
deleted file mode 100644
--- a/gfx/webrender/examples/alpha_perf.rs
+++ /dev/null
@@ -1,90 +0,0 @@
-/* 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/. */
-
-extern crate euclid;
-extern crate gleam;
-extern crate glutin;
-extern crate webrender;
-extern crate winit;
-
-#[path = "common/boilerplate.rs"]
-mod boilerplate;
-
-use boilerplate::{Example, HandyDandyRectBuilder};
-use std::cmp;
-use webrender::api::*;
-
-struct App {
-    rect_count: usize,
-}
-
-impl Example for App {
-    fn render(
-        &mut self,
-        _api: &RenderApi,
-        builder: &mut DisplayListBuilder,
-        _txn: &mut Transaction,
-        _framebuffer_size: DeviceUintSize,
-        _pipeline_id: PipelineId,
-        _document_id: DocumentId,
-    ) {
-        let bounds = (0, 0).to(1920, 1080);
-        let info = LayoutPrimitiveInfo::new(bounds);
-
-        builder.push_stacking_context(
-            &info,
-            None,
-            TransformStyle::Flat,
-            MixBlendMode::Normal,
-            Vec::new(),
-            GlyphRasterSpace::Screen,
-        );
-
-        for _ in 0 .. self.rect_count {
-            builder.push_rect(&info, ColorF::new(1.0, 1.0, 1.0, 0.05));
-        }
-
-        builder.pop_stacking_context();
-    }
-
-    fn on_event(
-        &mut self,
-        event: winit::WindowEvent,
-        _api: &RenderApi,
-        _document_id: DocumentId
-    ) -> bool {
-        match event {
-            winit::WindowEvent::KeyboardInput {
-                input: winit::KeyboardInput {
-                    state: winit::ElementState::Pressed,
-                    virtual_keycode: Some(key),
-                    ..
-                },
-                ..
-            } => {
-                match key {
-                    winit::VirtualKeyCode::Right => {
-                        self.rect_count += 1;
-                        println!("rects = {}", self.rect_count);
-                    }
-                    winit::VirtualKeyCode::Left => {
-                        self.rect_count = cmp::max(self.rect_count, 1) - 1;
-                        println!("rects = {}", self.rect_count);
-                    }
-                    _ => {}
-                };
-            }
-            _ => (),
-        }
-
-        true
-    }
-}
-
-fn main() {
-    let mut app = App {
-        rect_count: 1,
-    };
-    boilerplate::main_wrapper(&mut app, None);
-}
deleted file mode 100644
--- a/gfx/webrender/examples/animation.rs
+++ /dev/null
@@ -1,152 +0,0 @@
-/* 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/. */
-
-//! This example creates a 200x200 white rect and allows the user to move it
-//! around by using the arrow keys and rotate with '<'/'>'.
-//! It does this by using the animation API.
-
-//! The example also features seamless opaque/transparent split of a
-//! rounded cornered rectangle, which is done automatically during the
-//! scene building for render optimization.
-
-extern crate euclid;
-extern crate gleam;
-extern crate glutin;
-extern crate webrender;
-extern crate winit;
-
-#[path = "common/boilerplate.rs"]
-mod boilerplate;
-
-use boilerplate::{Example, HandyDandyRectBuilder};
-use euclid::Angle;
-use webrender::api::*;
-
-struct App {
-    property_key: PropertyBindingKey<LayoutTransform>,
-    opacity_key: PropertyBindingKey<f32>,
-    transform: LayoutTransform,
-    opacity: f32,
-}
-
-impl Example for App {
-    fn render(
-        &mut self,
-        _api: &RenderApi,
-        builder: &mut DisplayListBuilder,
-        _txn: &mut Transaction,
-        _framebuffer_size: DeviceUintSize,
-        _pipeline_id: PipelineId,
-        _document_id: DocumentId,
-    ) {
-        // Create a 200x200 stacking context with an animated transform property.
-        let bounds = (0, 0).to(200, 200);
-
-        let filters = vec![
-            FilterOp::Opacity(PropertyBinding::Binding(self.opacity_key, self.opacity), self.opacity),
-        ];
-
-        let info = LayoutPrimitiveInfo::new(bounds);
-        let reference_frame_id = builder.push_reference_frame(
-            &info,
-            Some(PropertyBinding::Binding(self.property_key, LayoutTransform::identity())),
-            None,
-        );
-
-        builder.push_clip_id(reference_frame_id);
-
-        let info = LayoutPrimitiveInfo::new(bounds);
-        builder.push_stacking_context(
-            &info,
-            None,
-            TransformStyle::Flat,
-            MixBlendMode::Normal,
-            filters,
-            GlyphRasterSpace::Screen,
-        );
-
-        let complex_clip = ComplexClipRegion {
-            rect: bounds,
-            radii: BorderRadius::uniform(50.0),
-            mode: ClipMode::Clip,
-        };
-        let clip_id = builder.define_clip(bounds, vec![complex_clip], None);
-        builder.push_clip_id(clip_id);
-
-        // Fill it with a white rect
-        builder.push_rect(&info, ColorF::new(0.0, 1.0, 0.0, 1.0));
-
-        builder.pop_clip_id();
-
-        builder.pop_stacking_context();
-
-        builder.pop_clip_id();
-        builder.pop_reference_frame();
-    }
-
-    fn on_event(&mut self, win_event: winit::WindowEvent, api: &RenderApi, document_id: DocumentId) -> bool {
-        match win_event {
-            winit::WindowEvent::KeyboardInput {
-                input: winit::KeyboardInput {
-                    state: winit::ElementState::Pressed,
-                    virtual_keycode: Some(key),
-                    ..
-                },
-                ..
-            } => {
-                let (offset_x, offset_y, angle, delta_opacity) = match key {
-                    winit::VirtualKeyCode::Down => (0.0, 10.0, 0.0, 0.0),
-                    winit::VirtualKeyCode::Up => (0.0, -10.0, 0.0, 0.0),
-                    winit::VirtualKeyCode::Right => (10.0, 0.0, 0.0, 0.0),
-                    winit::VirtualKeyCode::Left => (-10.0, 0.0, 0.0, 0.0),
-                    winit::VirtualKeyCode::Comma => (0.0, 0.0, 0.1, 0.0),
-                    winit::VirtualKeyCode::Period => (0.0, 0.0, -0.1, 0.0),
-                    winit::VirtualKeyCode::Z => (0.0, 0.0, 0.0, -0.1),
-                    winit::VirtualKeyCode::X => (0.0, 0.0, 0.0, 0.1),
-                    _ => return false,
-                };
-                // Update the transform based on the keyboard input and push it to
-                // webrender using the generate_frame API. This will recomposite with
-                // the updated transform.
-                self.opacity += delta_opacity;
-                let new_transform = self.transform
-                    .pre_rotate(0.0, 0.0, 1.0, Angle::radians(angle))
-                    .post_translate(LayoutVector3D::new(offset_x, offset_y, 0.0));
-                let mut txn = Transaction::new();
-                txn.update_dynamic_properties(
-                    DynamicProperties {
-                        transforms: vec![
-                            PropertyValue {
-                                key: self.property_key,
-                                value: new_transform,
-                            },
-                        ],
-                        floats: vec![
-                            PropertyValue {
-                                key: self.opacity_key,
-                                value: self.opacity,
-                            }
-                        ],
-                    },
-                );
-                txn.generate_frame();
-                api.send_transaction(document_id, txn);
-                self.transform = new_transform;
-            }
-            _ => (),
-        }
-
-        false
-    }
-}
-
-fn main() {
-    let mut app = App {
-        property_key: PropertyBindingKey::new(42), // arbitrary magic number
-        opacity_key: PropertyBindingKey::new(43),
-        transform: LayoutTransform::create_translation(0.0, 0.0, 0.0),
-        opacity: 0.5,
-    };
-    boilerplate::main_wrapper(&mut app, None);
-}
deleted file mode 100644
--- a/gfx/webrender/examples/basic.rs
+++ /dev/null
@@ -1,298 +0,0 @@
-/* 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/. */
-
-extern crate app_units;
-extern crate euclid;
-extern crate gleam;
-extern crate glutin;
-extern crate webrender;
-extern crate winit;
-
-#[path = "common/boilerplate.rs"]
-mod boilerplate;
-
-use boilerplate::{Example, HandyDandyRectBuilder};
-use euclid::vec2;
-use winit::TouchPhase;
-use std::collections::HashMap;
-use webrender::api::*;
-
-#[derive(Debug)]
-enum Gesture {
-    None,
-    Pan,
-    Zoom,
-}
-
-#[derive(Debug)]
-struct Touch {
-    id: u64,
-    start_x: f32,
-    start_y: f32,
-    current_x: f32,
-    current_y: f32,
-}
-
-fn dist(x0: f32, y0: f32, x1: f32, y1: f32) -> f32 {
-    let dx = x0 - x1;
-    let dy = y0 - y1;
-    ((dx * dx) + (dy * dy)).sqrt()
-}
-
-impl Touch {
-    fn distance_from_start(&self) -> f32 {
-        dist(self.start_x, self.start_y, self.current_x, self.current_y)
-    }
-
-    fn initial_distance_from_other(&self, other: &Touch) -> f32 {
-        dist(self.start_x, self.start_y, other.start_x, other.start_y)
-    }
-
-    fn current_distance_from_other(&self, other: &Touch) -> f32 {
-        dist(
-            self.current_x,
-            self.current_y,
-            other.current_x,
-            other.current_y,
-        )
-    }
-}
-
-struct TouchState {
-    active_touches: HashMap<u64, Touch>,
-    current_gesture: Gesture,
-    start_zoom: f32,
-    current_zoom: f32,
-    start_pan: DeviceIntPoint,
-    current_pan: DeviceIntPoint,
-}
-
-enum TouchResult {
-    None,
-    Pan(DeviceIntPoint),
-    Zoom(f32),
-}
-
-impl TouchState {
-    fn new() -> TouchState {
-        TouchState {
-            active_touches: HashMap::new(),
-            current_gesture: Gesture::None,
-            start_zoom: 1.0,
-            current_zoom: 1.0,
-            start_pan: DeviceIntPoint::zero(),
-            current_pan: DeviceIntPoint::zero(),
-        }
-    }
-
-    fn handle_event(&mut self, touch: winit::Touch) -> TouchResult {
-        match touch.phase {
-            TouchPhase::Started => {
-                debug_assert!(!self.active_touches.contains_key(&touch.id));
-                self.active_touches.insert(
-                    touch.id,
-                    Touch {
-                        id: touch.id,
-                        start_x: touch.location.0 as f32,
-                        start_y: touch.location.1 as f32,
-                        current_x: touch.location.0 as f32,
-                        current_y: touch.location.1 as f32,
-                    },
-                );
-                self.current_gesture = Gesture::None;
-            }
-            TouchPhase::Moved => {
-                match self.active_touches.get_mut(&touch.id) {
-                    Some(active_touch) => {
-                        active_touch.current_x = touch.location.0 as f32;
-                        active_touch.current_y = touch.location.1 as f32;
-                    }
-                    None => panic!("move touch event with unknown touch id!"),
-                }
-
-                match self.current_gesture {
-                    Gesture::None => {
-                        let mut over_threshold_count = 0;
-                        let active_touch_count = self.active_touches.len();
-
-                        for (_, touch) in &self.active_touches {
-                            if touch.distance_from_start() > 8.0 {
-                                over_threshold_count += 1;
-                            }
-                        }
-
-                        if active_touch_count == over_threshold_count {
-                            if active_touch_count == 1 {
-                                self.start_pan = self.current_pan;
-                                self.current_gesture = Gesture::Pan;
-                            } else if active_touch_count == 2 {
-                                self.start_zoom = self.current_zoom;
-                                self.current_gesture = Gesture::Zoom;
-                            }
-                        }
-                    }
-                    Gesture::Pan => {
-                        let keys: Vec<u64> = self.active_touches.keys().cloned().collect();
-                        debug_assert!(keys.len() == 1);
-                        let active_touch = &self.active_touches[&keys[0]];
-                        let x = active_touch.current_x - active_touch.start_x;
-                        let y = active_touch.current_y - active_touch.start_y;
-                        self.current_pan.x = self.start_pan.x + x.round() as i32;
-                        self.current_pan.y = self.start_pan.y + y.round() as i32;
-                        return TouchResult::Pan(self.current_pan);
-                    }
-                    Gesture::Zoom => {
-                        let keys: Vec<u64> = self.active_touches.keys().cloned().collect();
-                        debug_assert!(keys.len() == 2);
-                        let touch0 = &self.active_touches[&keys[0]];
-                        let touch1 = &self.active_touches[&keys[1]];
-                        let initial_distance = touch0.initial_distance_from_other(touch1);
-                        let current_distance = touch0.current_distance_from_other(touch1);
-                        self.current_zoom = self.start_zoom * current_distance / initial_distance;
-                        return TouchResult::Zoom(self.current_zoom);
-                    }
-                }
-            }
-            TouchPhase::Ended | TouchPhase::Cancelled => {
-                self.active_touches.remove(&touch.id).unwrap();
-                self.current_gesture = Gesture::None;
-            }
-        }
-
-        TouchResult::None
-    }
-}
-
-fn main() {
-    let mut app = App {
-        touch_state: TouchState::new(),
-    };
-    boilerplate::main_wrapper(&mut app, None);
-}
-
-struct App {
-    touch_state: TouchState,
-}
-
-impl Example for App {
-    // Make this the only example to test all shaders for compile errors.
-    const PRECACHE_SHADERS: bool = true;
-
-    fn render(
-        &mut self,
-        api: &RenderApi,
-        builder: &mut DisplayListBuilder,
-        txn: &mut Transaction,
-        _: DeviceUintSize,
-        _pipeline_id: PipelineId,
-        _document_id: DocumentId,
-    ) {
-        let bounds = LayoutRect::new(LayoutPoint::zero(), builder.content_size());
-        let info = LayoutPrimitiveInfo::new(bounds);
-        builder.push_stacking_context(
-            &info,
-            None,
-            TransformStyle::Flat,
-            MixBlendMode::Normal,
-            Vec::new(),
-            GlyphRasterSpace::Screen,
-        );
-
-        let image_mask_key = api.generate_image_key();
-        txn.add_image(
-            image_mask_key,
-            ImageDescriptor::new(2, 2, ImageFormat::R8, true, false),
-            ImageData::new(vec![0, 80, 180, 255]),
-            None,
-        );
-        let mask = ImageMask {
-            image: image_mask_key,
-            rect: (75, 75).by(100, 100),
-            repeat: false,
-        };
-        let complex = ComplexClipRegion::new(
-            (50, 50).to(150, 150),
-            BorderRadius::uniform(20.0),
-            ClipMode::Clip
-        );
-        let id = builder.define_clip(bounds, vec![complex], Some(mask));
-        builder.push_clip_id(id);
-
-        let info = LayoutPrimitiveInfo::new((100, 100).to(200, 200));
-        builder.push_rect(&info, ColorF::new(0.0, 1.0, 0.0, 1.0));
-
-        let info = LayoutPrimitiveInfo::new((250, 100).to(350, 200));
-        builder.push_rect(&info, ColorF::new(0.0, 1.0, 0.0, 1.0));
-        let border_side = BorderSide {
-            color: ColorF::new(0.0, 0.0, 1.0, 1.0),
-            style: BorderStyle::Groove,
-        };
-        let border_widths = BorderWidths {
-            top: 10.0,
-            left: 10.0,
-            bottom: 10.0,
-            right: 10.0,
-        };
-        let border_details = BorderDetails::Normal(NormalBorder {
-            top: border_side,
-            right: border_side,
-            bottom: border_side,
-            left: border_side,
-            radius: BorderRadius::uniform(20.0),
-        });
-
-        let info = LayoutPrimitiveInfo::new((100, 100).to(200, 200));
-        builder.push_border(&info, border_widths, border_details);
-        builder.pop_clip_id();
-
-        if false {
-            // draw box shadow?
-            let rect = LayoutRect::zero();
-            let simple_box_bounds = (20, 200).by(50, 50);
-            let offset = vec2(10.0, 10.0);
-            let color = ColorF::new(1.0, 1.0, 1.0, 1.0);
-            let blur_radius = 0.0;
-            let spread_radius = 0.0;
-            let simple_border_radius = 8.0;
-            let box_shadow_type = BoxShadowClipMode::Inset;
-            let info = LayoutPrimitiveInfo::with_clip_rect(rect, bounds);
-
-            builder.push_box_shadow(
-                &info,
-                simple_box_bounds,
-                offset,
-                color,
-                blur_radius,
-                spread_radius,
-                BorderRadius::uniform(simple_border_radius),
-                box_shadow_type,
-            );
-        }
-
-        builder.pop_stacking_context();
-    }
-
-    fn on_event(&mut self, event: winit::WindowEvent, api: &RenderApi, document_id: DocumentId) -> bool {
-        let mut txn = Transaction::new();
-        match event {
-            winit::WindowEvent::Touch(touch) => match self.touch_state.handle_event(touch) {
-                TouchResult::Pan(pan) => {
-                    txn.set_pan(pan);
-                }
-                TouchResult::Zoom(zoom) => {
-                    txn.set_pinch_zoom(ZoomFactor::new(zoom));
-                }
-                TouchResult::None => {}
-            },
-            _ => (),
-        }
-
-        if !txn.is_empty() {
-            txn.generate_frame();
-            api.send_transaction(document_id, txn);
-        }
-
-        false
-    }
-}
deleted file mode 100644
--- a/gfx/webrender/examples/blob.rs
+++ /dev/null
@@ -1,302 +0,0 @@
-/* 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/. */
-
-extern crate gleam;
-extern crate glutin;
-extern crate rayon;
-extern crate webrender;
-extern crate winit;
-
-#[path = "common/boilerplate.rs"]
-mod boilerplate;
-
-use boilerplate::{Example, HandyDandyRectBuilder};
-use rayon::{ThreadPool, ThreadPoolBuilder};
-use std::collections::HashMap;
-use std::collections::hash_map::Entry;
-use std::sync::Arc;
-use std::sync::mpsc::{Receiver, Sender, channel};
-use webrender::api::{self, DisplayListBuilder, DocumentId, PipelineId, RenderApi, Transaction};
-
-// This example shows how to implement a very basic BlobImageRenderer that can only render
-// a checkerboard pattern.
-
-// The deserialized command list internally used by this example is just a color.
-type ImageRenderingCommands = api::ColorU;
-
-// Serialize/deserialize the blob.
-// For real usecases you should probably use serde rather than doing it by hand.
-
-fn serialize_blob(color: api::ColorU) -> Vec<u8> {
-    vec![color.r, color.g, color.b, color.a]
-}
-
-fn deserialize_blob(blob: &[u8]) -> Result<ImageRenderingCommands, ()> {
-    let mut iter = blob.iter();
-    return match (iter.next(), iter.next(), iter.next(), iter.next()) {
-        (Some(&r), Some(&g), Some(&b), Some(&a)) => Ok(api::ColorU::new(r, g, b, a)),
-        (Some(&a), None, None, None) => Ok(api::ColorU::new(a, a, a, a)),
-        _ => Err(()),
-    };
-}
-
-// This is the function that applies the deserialized drawing commands and generates
-// actual image data.
-fn render_blob(
-    commands: Arc<ImageRenderingCommands>,
-    descriptor: &api::BlobImageDescriptor,
-    tile: Option<api::TileOffset>,
-) -> api::BlobImageResult {
-    let color = *commands;
-
-    // Allocate storage for the result. Right now the resource cache expects the
-    // tiles to have have no stride or offset.
-    let mut texels = Vec::with_capacity((descriptor.size.width * descriptor.size.height * 4) as usize);
-
-    // Generate a per-tile pattern to see it in the demo. For a real use case it would not
-    // make sense for the rendered content to depend on its tile.
-    let tile_checker = match tile {
-        Some(tile) => (tile.x % 2 == 0) != (tile.y % 2 == 0),
-        None => true,
-    };
-
-    for y in 0 .. descriptor.size.height {
-        for x in 0 .. descriptor.size.width {
-            // Apply the tile's offset. This is important: all drawing commands should be
-            // translated by this offset to give correct results with tiled blob images.
-            let x2 = x + descriptor.offset.x as u32;
-            let y2 = y + descriptor.offset.y as u32;
-
-            // Render a simple checkerboard pattern
-            let checker = if (x2 % 20 >= 10) != (y2 % 20 >= 10) {
-                1
-            } else {
-                0
-            };
-            // ..nested in the per-tile checkerboard pattern
-            let tc = if tile_checker { 0 } else { (1 - checker) * 40 };
-
-            match descriptor.format {
-                api::ImageFormat::BGRA8 => {
-                    texels.push(color.b * checker + tc);
-                    texels.push(color.g * checker + tc);
-                    texels.push(color.r * checker + tc);
-                    texels.push(color.a * checker + tc);
-                }
-                api::ImageFormat::R8 => {
-                    texels.push(color.a * checker + tc);
-                }
-                _ => {
-                    return Err(api::BlobImageError::Other(
-                        format!("Unsupported image format"),
-                    ));
-                }
-            }
-        }
-    }
-
-    Ok(api::RasterizedBlobImage {
-        data: texels,
-        size: descriptor.size,
-    })
-}
-
-struct CheckerboardRenderer {
-    // We are going to defer the rendering work to worker threads.
-    // Using a pre-built Arc<ThreadPool> rather than creating our own threads
-    // makes it possible to share the same thread pool as the glyph renderer (if we
-    // want to).
-    workers: Arc<ThreadPool>,
-
-    // the workers will use an mpsc channel to communicate the result.
-    tx: Sender<(api::BlobImageRequest, api::BlobImageResult)>,
-    rx: Receiver<(api::BlobImageRequest, api::BlobImageResult)>,
-
-    // The deserialized drawing commands.
-    // In this example we store them in Arcs. This isn't necessary since in this simplified
-    // case the command list is a simple 32 bits value and would be cheap to clone before sending
-    // to the workers. But in a more realistic scenario the commands would typically be bigger
-    // and more expensive to clone, so let's pretend it is also the case here.
-    image_cmds: HashMap<api::ImageKey, Arc<ImageRenderingCommands>>,
-
-    // The images rendered in the current frame (not kept here between frames).
-    rendered_images: HashMap<api::BlobImageRequest, Option<api::BlobImageResult>>,
-}
-
-impl CheckerboardRenderer {
-    fn new(workers: Arc<ThreadPool>) -> Self {
-        let (tx, rx) = channel();
-        CheckerboardRenderer {
-            image_cmds: HashMap::new(),
-            rendered_images: HashMap::new(),
-            workers,
-            tx,
-            rx,
-        }
-    }
-}
-
-impl api::BlobImageRenderer for CheckerboardRenderer {
-    fn add(&mut self, key: api::ImageKey, cmds: Arc<api::BlobImageData>, _: Option<api::TileSize>) {
-        self.image_cmds
-            .insert(key, Arc::new(deserialize_blob(&cmds[..]).unwrap()));
-    }
-
-    fn update(&mut self, key: api::ImageKey, cmds: Arc<api::BlobImageData>, _dirty_rect: Option<api::DeviceUintRect>) {
-        // Here, updating is just replacing the current version of the commands with
-        // the new one (no incremental updates).
-        self.image_cmds
-            .insert(key, Arc::new(deserialize_blob(&cmds[..]).unwrap()));
-    }
-
-    fn delete(&mut self, key: api::ImageKey) {
-        self.image_cmds.remove(&key);
-    }
-
-    fn request(
-        &mut self,
-        _resources: &api::BlobImageResources,
-        request: api::BlobImageRequest,
-        descriptor: &api::BlobImageDescriptor,
-        _dirty_rect: Option<api::DeviceUintRect>,
-    ) {
-        // This method is where we kick off our rendering jobs.
-        // It should avoid doing work on the calling thread as much as possible.
-        // In this example we will use the thread pool to render individual tiles.
-
-        // Gather the input data to send to a worker thread.
-        let cmds = Arc::clone(&self.image_cmds.get(&request.key).unwrap());
-        let tx = self.tx.clone();
-        let descriptor = descriptor.clone();
-
-        self.workers.spawn(move || {
-            let result = render_blob(cmds, &descriptor, request.tile);
-            tx.send((request, result)).unwrap();
-        });
-
-        // Add None in the map of rendered images. This makes it possible to differentiate
-        // between commands that aren't finished yet (entry in the map is equal to None) and
-        // keys that have never been requested (entry not in the map), which would cause deadlocks
-        // if we were to block upon receiving their result in resolve!
-        self.rendered_images.insert(request, None);
-    }
-
-    fn resolve(&mut self, request: api::BlobImageRequest) -> api::BlobImageResult {
-        // In this method we wait until the work is complete on the worker threads and
-        // gather the results.
-
-        // First look at whether we have already received the rendered image
-        // that we are looking for.
-        match self.rendered_images.entry(request) {
-            Entry::Vacant(_) => {
-                return Err(api::BlobImageError::InvalidKey);
-            }
-            Entry::Occupied(entry) => {
-                // None means we haven't yet received the result.
-                if entry.get().is_some() {
-                    let result = entry.remove();
-                    return result.unwrap();
-                }
-            }
-        }
-
-        // We haven't received it yet, pull from the channel until we receive it.
-        while let Ok((req, result)) = self.rx.recv() {
-            if req == request {
-                // There it is!
-                return result;
-            }
-            self.rendered_images.insert(req, Some(result));
-        }
-
-        // If we break out of the loop above it means the channel closed unexpectedly.
-        Err(api::BlobImageError::Other("Channel closed".into()))
-    }
-    fn delete_font(&mut self, _font: api::FontKey) {}
-    fn delete_font_instance(&mut self, _instance: api::FontInstanceKey) {}
-    fn clear_namespace(&mut self, _namespace: api::IdNamespace) {}
-}
-
-struct App {}
-
-impl Example for App {
-    fn render(
-        &mut self,
-        api: &RenderApi,
-        builder: &mut DisplayListBuilder,
-        txn: &mut Transaction,
-        _framebuffer_size: api::DeviceUintSize,
-        _pipeline_id: PipelineId,
-        _document_id: DocumentId,
-    ) {
-        let blob_img1 = api.generate_image_key();
-        txn.add_image(
-            blob_img1,
-            api::ImageDescriptor::new(500, 500, api::ImageFormat::BGRA8, true, false),
-            api::ImageData::new_blob_image(serialize_blob(api::ColorU::new(50, 50, 150, 255))),
-            Some(128),
-        );
-
-        let blob_img2 = api.generate_image_key();
-        txn.add_image(
-            blob_img2,
-            api::ImageDescriptor::new(200, 200, api::ImageFormat::BGRA8, true, false),
-            api::ImageData::new_blob_image(serialize_blob(api::ColorU::new(50, 150, 50, 255))),
-            None,
-        );
-
-        let bounds = api::LayoutRect::new(api::LayoutPoint::zero(), builder.content_size());
-        let info = api::LayoutPrimitiveInfo::new(bounds);
-        builder.push_stacking_context(
-            &info,
-            None,
-            api::TransformStyle::Flat,
-            api::MixBlendMode::Normal,
-            Vec::new(),
-            api::GlyphRasterSpace::Screen,
-        );
-
-        let info = api::LayoutPrimitiveInfo::new((30, 30).by(500, 500));
-        builder.push_image(
-            &info,
-            api::LayoutSize::new(500.0, 500.0),
-            api::LayoutSize::new(0.0, 0.0),
-            api::ImageRendering::Auto,
-            api::AlphaType::PremultipliedAlpha,
-            blob_img1,
-        );
-
-        let info = api::LayoutPrimitiveInfo::new((600, 600).by(200, 200));
-        builder.push_image(
-            &info,
-            api::LayoutSize::new(200.0, 200.0),
-            api::LayoutSize::new(0.0, 0.0),
-            api::ImageRendering::Auto,
-            api::AlphaType::PremultipliedAlpha,
-            blob_img2,
-        );
-
-        builder.pop_stacking_context();
-    }
-}
-
-fn main() {
-    let workers =
-        ThreadPoolBuilder::new().thread_name(|idx| format!("WebRender:Worker#{}", idx))
-                                .build();
-
-    let workers = Arc::new(workers.unwrap());
-
-    let opts = webrender::RendererOptions {
-        workers: Some(Arc::clone(&workers)),
-        // Register our blob renderer, so that WebRender integrates it in the resource cache..
-        // Share the same pool of worker threads between WebRender and our blob renderer.
-        blob_image_renderer: Some(Box::new(CheckerboardRenderer::new(Arc::clone(&workers)))),
-        ..Default::default()
-    };
-
-    let mut app = App {};
-
-    boilerplate::main_wrapper(&mut app, Some(opts));
-}
deleted file mode 100644
--- a/gfx/webrender/examples/common/boilerplate.rs
+++ /dev/null
@@ -1,286 +0,0 @@
-/* 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/. */
-
-extern crate env_logger;
-extern crate euclid;
-
-use gleam::gl;
-use glutin::{self, GlContext};
-use std::env;
-use std::path::PathBuf;
-use webrender;
-use winit;
-use webrender::api::*;
-
-struct Notifier {
-    events_proxy: winit::EventsLoopProxy,
-}
-
-impl Notifier {
-    fn new(events_proxy: winit::EventsLoopProxy) -> Notifier {
-        Notifier { events_proxy }
-    }
-}
-
-impl RenderNotifier for Notifier {
-    fn clone(&self) -> Box<RenderNotifier> {
-        Box::new(Notifier {
-            events_proxy: self.events_proxy.clone(),
-        })
-    }
-
-    fn wake_up(&self) {
-        #[cfg(not(target_os = "android"))]
-        let _ = self.events_proxy.wakeup();
-    }
-
-    fn new_frame_ready(&self, _: DocumentId, _scrolled: bool, _composite_needed: bool) {
-        self.wake_up();
-    }
-}
-
-pub trait HandyDandyRectBuilder {
-    fn to(&self, x2: i32, y2: i32) -> LayoutRect;
-    fn by(&self, w: i32, h: i32) -> LayoutRect;
-}
-// Allows doing `(x, y).to(x2, y2)` or `(x, y).by(width, height)` with i32
-// values to build a f32 LayoutRect
-impl HandyDandyRectBuilder for (i32, i32) {
-    fn to(&self, x2: i32, y2: i32) -> LayoutRect {
-        LayoutRect::new(
-            LayoutPoint::new(self.0 as f32, self.1 as f32),
-            LayoutSize::new((x2 - self.0) as f32, (y2 - self.1) as f32),
-        )
-    }
-
-    fn by(&self, w: i32, h: i32) -> LayoutRect {
-        LayoutRect::new(
-            LayoutPoint::new(self.0 as f32, self.1 as f32),
-            LayoutSize::new(w as f32, h as f32),
-        )
-    }
-}
-
-pub trait Example {
-    const TITLE: &'static str = "WebRender Sample App";
-    const PRECACHE_SHADERS: bool = false;
-    const WIDTH: u32 = 1920;
-    const HEIGHT: u32 = 1080;
-
-    fn render(
-        &mut self,
-        api: &RenderApi,
-        builder: &mut DisplayListBuilder,
-        txn: &mut Transaction,
-        framebuffer_size: DeviceUintSize,
-        pipeline_id: PipelineId,
-        document_id: DocumentId,
-    );
-    fn on_event(&mut self, winit::WindowEvent, &RenderApi, DocumentId) -> bool {
-        false
-    }
-    fn get_image_handlers(
-        &mut self,
-        _gl: &gl::Gl,
-    ) -> (Option<Box<webrender::ExternalImageHandler>>,
-          Option<Box<webrender::OutputImageHandler>>) {
-        (None, None)
-    }
-    fn draw_custom(&self, _gl: &gl::Gl) {
-    }
-}
-
-pub fn main_wrapper<E: Example>(
-    example: &mut E,
-    options: Option<webrender::RendererOptions>,
-) {
-    env_logger::init();
-
-    let args: Vec<String> = env::args().collect();
-    let res_path = if args.len() > 1 {
-        Some(PathBuf::from(&args[1]))
-    } else {
-        None
-    };
-
-    let mut events_loop = winit::EventsLoop::new();
-    let context_builder = glutin::ContextBuilder::new()
-        .with_gl(glutin::GlRequest::GlThenGles {
-            opengl_version: (3, 2),
-            opengles_version: (3, 0),
-        });
-    let window_builder = winit::WindowBuilder::new()
-        .with_title(E::TITLE)
-        .with_multitouch()
-        .with_dimensions(E::WIDTH, E::HEIGHT);
-    let window = glutin::GlWindow::new(window_builder, context_builder, &events_loop)
-        .unwrap();
-
-    unsafe {
-        window.make_current().ok();
-    }
-
-    let gl = match window.get_api() {
-        glutin::Api::OpenGl => unsafe {
-            gl::GlFns::load_with(|symbol| window.get_proc_address(symbol) as *const _)
-        },
-        glutin::Api::OpenGlEs => unsafe {
-            gl::GlesFns::load_with(|symbol| window.get_proc_address(symbol) as *const _)
-        },
-        glutin::Api::WebGl => unimplemented!(),
-    };
-
-    println!("OpenGL version {}", gl.get_string(gl::VERSION));
-    println!("Shader resource path: {:?}", res_path);
-    let device_pixel_ratio = window.hidpi_factor();
-    println!("Device pixel ratio: {}", device_pixel_ratio);
-
-    println!("Loading shaders...");
-    let opts = webrender::RendererOptions {
-        resource_override_path: res_path,
-        precache_shaders: E::PRECACHE_SHADERS,
-        device_pixel_ratio,
-        clear_color: Some(ColorF::new(0.3, 0.0, 0.0, 1.0)),
-        //scatter_gpu_cache_updates: false,
-        debug_flags: webrender::DebugFlags::ECHO_DRIVER_MESSAGES,
-        ..options.unwrap_or(webrender::RendererOptions::default())
-    };
-
-    let framebuffer_size = {
-        let (width, height) = window.get_inner_size().unwrap();
-        DeviceUintSize::new(width, height)
-    };
-    let notifier = Box::new(Notifier::new(events_loop.create_proxy()));
-    let (mut renderer, sender) = webrender::Renderer::new(gl.clone(), notifier, opts).unwrap();
-    let api = sender.create_api();
-    let document_id = api.add_document(framebuffer_size, 0);
-
-    let (external, output) = example.get_image_handlers(&*gl);
-
-    if let Some(output_image_handler) = output {
-        renderer.set_output_image_handler(output_image_handler);
-    }
-
-    if let Some(external_image_handler) = external {
-        renderer.set_external_image_handler(external_image_handler);
-    }
-
-    let epoch = Epoch(0);
-    let pipeline_id = PipelineId(0, 0);
-    let layout_size = framebuffer_size.to_f32() / euclid::TypedScale::new(device_pixel_ratio);
-    let mut builder = DisplayListBuilder::new(pipeline_id, layout_size);
-    let mut txn = Transaction::new();
-
-    example.render(
-        &api,
-        &mut builder,
-        &mut txn,
-        framebuffer_size,
-        pipeline_id,
-        document_id,
-    );
-    txn.set_display_list(
-        epoch,
-        None,
-        layout_size,
-        builder.finalize(),
-        true,
-    );
-    txn.set_root_pipeline(pipeline_id);
-    txn.generate_frame();
-    api.send_transaction(document_id, txn);
-
-    println!("Entering event loop");
-    events_loop.run_forever(|global_event| {
-        let mut txn = Transaction::new();
-        let mut custom_event = true;
-
-        match global_event {
-            winit::Event::WindowEvent {
-                event: winit::WindowEvent::CloseRequested,
-                ..
-            } => return winit::ControlFlow::Break,
-            winit::Event::WindowEvent {
-                event: winit::WindowEvent::KeyboardInput {
-                    input: winit::KeyboardInput {
-                        state: winit::ElementState::Pressed,
-                        virtual_keycode: Some(key),
-                        ..
-                    },
-                    ..
-                },
-                ..
-            } => match key {
-                winit::VirtualKeyCode::Escape => return winit::ControlFlow::Break,
-                winit::VirtualKeyCode::P => renderer.toggle_debug_flags(webrender::DebugFlags::PROFILER_DBG),
-                winit::VirtualKeyCode::O => renderer.toggle_debug_flags(webrender::DebugFlags::RENDER_TARGET_DBG),
-                winit::VirtualKeyCode::I => renderer.toggle_debug_flags(webrender::DebugFlags::TEXTURE_CACHE_DBG),
-                winit::VirtualKeyCode::S => renderer.toggle_debug_flags(webrender::DebugFlags::COMPACT_PROFILER),
-                winit::VirtualKeyCode::Q => renderer.toggle_debug_flags(
-                    webrender::DebugFlags::GPU_TIME_QUERIES | webrender::DebugFlags::GPU_SAMPLE_QUERIES
-                ),
-                winit::VirtualKeyCode::Key1 => txn.set_window_parameters(
-                    framebuffer_size,
-                    DeviceUintRect::new(DeviceUintPoint::zero(), framebuffer_size),
-                    1.0
-                ),
-                winit::VirtualKeyCode::Key2 => txn.set_window_parameters(
-                    framebuffer_size,
-                    DeviceUintRect::new(DeviceUintPoint::zero(), framebuffer_size),
-                    2.0
-                ),
-                winit::VirtualKeyCode::M => api.notify_memory_pressure(),
-                #[cfg(feature = "capture")]
-                winit::VirtualKeyCode::C => {
-                    let path: PathBuf = "../captures/example".into();
-                    //TODO: switch between SCENE/FRAME capture types
-                    // based on "shift" modifier, when `glutin` is updated.
-                    let bits = CaptureBits::all();
-                    api.save_capture(path, bits);
-                },
-                _ => {
-                    let win_event = match global_event {
-                        winit::Event::WindowEvent { event, .. } => event,
-                        _ => unreachable!()
-                    };
-                    custom_event = example.on_event(win_event, &api, document_id)
-                },
-            },
-            winit::Event::WindowEvent { event, .. } => custom_event = example.on_event(event, &api, document_id),
-            _ => return winit::ControlFlow::Continue,
-        };
-
-        if custom_event {
-            let mut builder = DisplayListBuilder::new(pipeline_id, layout_size);
-
-            example.render(
-                &api,
-                &mut builder,
-                &mut txn,
-                framebuffer_size,
-                pipeline_id,
-                document_id,
-            );
-            txn.set_display_list(
-                epoch,
-                None,
-                layout_size,
-                builder.finalize(),
-                true,
-            );
-            txn.generate_frame();
-        }
-        api.send_transaction(document_id, txn);
-
-        renderer.update();
-        renderer.render(framebuffer_size).unwrap();
-        let _ = renderer.flush_pipeline_info();
-        example.draw_custom(&*gl);
-        window.swap_buffers().ok();
-
-        winit::ControlFlow::Continue
-    });
-
-    renderer.deinit();
-}
deleted file mode 100644
--- a/gfx/webrender/examples/common/image_helper.rs
+++ /dev/null
@@ -1,16 +0,0 @@
-/* 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 webrender::api::{ImageData, ImageDescriptor, ImageFormat};
-
-pub fn make_checkerboard(width: u32, height: u32) -> (ImageDescriptor, ImageData) {
-    let mut image_data = Vec::new();
-    for y in 0 .. height {
-        for x in 0 .. width {
-            let lum = 255 * (((x & 8) == 0) ^ ((y & 8) == 0)) as u8;
-            image_data.extend_from_slice(&[lum, lum, lum, 0xff]);
-        }
-    }
-    (ImageDescriptor::new(width, height, ImageFormat::BGRA8, true, false), ImageData::new(image_data))
-}
deleted file mode 100644
--- a/gfx/webrender/examples/document.rs
+++ /dev/null
@@ -1,147 +0,0 @@
-/* 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/. */
-
-extern crate euclid;
-extern crate gleam;
-extern crate glutin;
-extern crate webrender;
-extern crate winit;
-
-#[path = "common/boilerplate.rs"]
-mod boilerplate;
-
-use boilerplate::Example;
-use euclid::TypedScale;
-use webrender::api::*;
-
-// This example creates multiple documents overlapping each other with
-// specified layer indices.
-
-struct Document {
-    id: DocumentId,
-    pipeline_id: PipelineId,
-    content_rect: LayoutRect,
-    color: ColorF,
-}
-
-struct App {
-    documents: Vec<Document>,
-}
-
-impl App {
-    fn init(
-        &mut self,
-        api: &RenderApi,
-        framebuffer_size: DeviceUintSize,
-        device_pixel_ratio: f32,
-    ) {
-        let init_data = vec![
-            (
-                PipelineId(1, 0),
-                -1,
-                ColorF::new(0.0, 1.0, 0.0, 1.0),
-                DeviceUintPoint::new(0, 0),
-            ),
-            (
-                PipelineId(2, 0),
-                -2,
-                ColorF::new(1.0, 1.0, 0.0, 1.0),
-                DeviceUintPoint::new(200, 0),
-            ),
-            (
-                PipelineId(3, 0),
-                -3,
-                ColorF::new(1.0, 0.0, 0.0, 1.0),
-                DeviceUintPoint::new(200, 200),
-            ),
-            (
-                PipelineId(4, 0),
-                -4,
-                ColorF::new(1.0, 0.0, 1.0, 1.0),
-                DeviceUintPoint::new(0, 200),
-            ),
-        ];
-
-        for (pipeline_id, layer, color, offset) in init_data {
-            let size = DeviceUintSize::new(250, 250);
-            let bounds = DeviceUintRect::new(offset, size);
-
-            let document_id = api.add_document(size, layer);
-            let mut txn = Transaction::new();
-            txn.set_window_parameters(framebuffer_size, bounds, device_pixel_ratio);
-            txn.set_root_pipeline(pipeline_id);
-            api.send_transaction(document_id, txn);
-
-            self.documents.push(Document {
-                id: document_id,
-                pipeline_id,
-                content_rect: bounds.to_f32() / TypedScale::new(device_pixel_ratio),
-                color,
-            });
-        }
-    }
-}
-
-impl Example for App {
-    fn render(
-        &mut self,
-        api: &RenderApi,
-        base_builder: &mut DisplayListBuilder,
-        _txn: &mut Transaction,
-        framebuffer_size: DeviceUintSize,
-        _: PipelineId,
-        _: DocumentId,
-    ) {
-        if self.documents.is_empty() {
-            let device_pixel_ratio = framebuffer_size.width as f32 /
-                base_builder.content_size().width;
-            // this is the first run, hack around the boilerplate,
-            // which assumes an example only needs one document
-            self.init(api, framebuffer_size, device_pixel_ratio);
-        }
-
-        for doc in &self.documents {
-            let mut builder = DisplayListBuilder::new(
-                doc.pipeline_id,
-                doc.content_rect.size,
-            );
-            let local_rect = LayoutRect::new(
-                LayoutPoint::zero(),
-                doc.content_rect.size,
-            );
-
-            builder.push_stacking_context(
-                &LayoutPrimitiveInfo::new(doc.content_rect),
-                None,
-                TransformStyle::Flat,
-                MixBlendMode::Normal,
-                Vec::new(),
-                GlyphRasterSpace::Screen,
-            );
-            builder.push_rect(
-                &LayoutPrimitiveInfo::new(local_rect),
-                doc.color,
-            );
-            builder.pop_stacking_context();
-
-            let mut txn = Transaction::new();
-            txn.set_display_list(
-                Epoch(0),
-                None,
-                doc.content_rect.size,
-                builder.finalize(),
-                true,
-            );
-            txn.generate_frame();
-            api.send_transaction(doc.id, txn);
-        }
-    }
-}
-
-fn main() {
-    let mut app = App {
-        documents: Vec::new(),
-    };
-    boilerplate::main_wrapper(&mut app, None);
-}
deleted file mode 100644
--- a/gfx/webrender/examples/frame_output.rs
+++ /dev/null
@@ -1,222 +0,0 @@
-/* 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/. */
-
-extern crate euclid;
-extern crate gleam;
-extern crate glutin;
-extern crate webrender;
-extern crate winit;
-
-#[path = "common/boilerplate.rs"]
-mod boilerplate;
-
-use boilerplate::{Example, HandyDandyRectBuilder};
-use euclid::TypedScale;
-use gleam::gl;
-use webrender::api::*;
-
-// This example demonstrates using the frame output feature to copy
-// the output of a WR framebuffer to a custom texture.
-
-#[derive(Debug)]
-struct Document {
-    id: DocumentId,
-    pipeline_id: PipelineId,
-    content_rect: LayoutRect,
-    color: ColorF,
-}
-
-
-struct App {
-    external_image_key: Option<ImageKey>,
-    output_document: Option<Document>
-}
-
-struct OutputHandler {
-    texture_id: gl::GLuint
-}
-
-struct ExternalHandler {
-    texture_id: gl::GLuint
-}
-
-impl webrender::OutputImageHandler for OutputHandler {
-    fn lock(&mut self, _id: PipelineId) -> Option<(u32, DeviceIntSize)> {
-        Some((self.texture_id, DeviceIntSize::new(500, 500)))
-    }
-
-    fn unlock(&mut self, _id: PipelineId) {}
-}
-
-impl webrender::ExternalImageHandler for ExternalHandler {
-    fn lock(&mut self, _key: ExternalImageId, _channel_index: u8) -> webrender::ExternalImage {
-        webrender::ExternalImage {
-            uv: TexelRect::new(0.0, 0.0, 1.0, 1.0),
-            source: webrender::ExternalImageSource::NativeTexture(self.texture_id),
-        }
-    }
-    fn unlock(&mut self, _key: ExternalImageId, _channel_index: u8) {}
-}
-
-impl App {
-    fn init_output_document(
-        &mut self,
-        api: &RenderApi,
-        framebuffer_size: DeviceUintSize,
-        device_pixel_ratio: f32,
-    ) {
-        // Generate the external image key that will be used to render the output document to the root document.
-        self.external_image_key = Some(api.generate_image_key());
-        let mut txn = Transaction::new();
-        txn.add_image(
-            self.external_image_key.unwrap(),
-            ImageDescriptor::new(100, 100, ImageFormat::BGRA8, true, false),
-            ImageData::External(ExternalImageData {
-                id: ExternalImageId(0),
-                channel_index: 0,
-                image_type: ExternalImageType::TextureHandle(TextureTarget::Default),
-            }),
-            None,
-        );
-
-        let pipeline_id = PipelineId(1, 0);
-        let layer = 1;
-        let color = ColorF::new(1., 1., 0., 1.);
-        let bounds = DeviceUintRect::new(DeviceUintPoint::zero(), framebuffer_size);
-        let document_id = api.add_document(framebuffer_size, layer);
-
-        let document = Document {
-            id: document_id,
-            pipeline_id,
-            content_rect: bounds.to_f32() / TypedScale::new(device_pixel_ratio),
-            color,
-        };
-
-        let info = LayoutPrimitiveInfo::new(document.content_rect);
-        let mut builder = DisplayListBuilder::new(
-            document.pipeline_id,
-            document.content_rect.size,
-        );
-
-        builder.push_stacking_context(
-            &info,
-            None,
-            TransformStyle::Flat,
-            MixBlendMode::Normal,
-            Vec::new(),
-            GlyphRasterSpace::Screen,
-        );
-
-        builder.push_rect(&info, ColorF::new(1.0, 1.0, 0.0, 1.0));
-        builder.pop_stacking_context();
-
-        txn.set_root_pipeline(pipeline_id);
-        txn.enable_frame_output(document.pipeline_id, true);
-        txn.set_display_list(
-            Epoch(0),
-            Some(document.color),
-            document.content_rect.size,
-            builder.finalize(),
-            true,
-        );
-        txn.generate_frame();
-        api.send_transaction(document.id, txn);
-        self.output_document = Some(document);
-    }
-}
-
-impl Example for App {
-    fn render(
-        &mut self,
-        api: &RenderApi,
-        builder: &mut DisplayListBuilder,
-        _txn: &mut Transaction,
-        framebuffer_size: DeviceUintSize,
-        _pipeline_id: PipelineId,
-        _document_id: DocumentId,
-    ) {
-        if self.output_document.is_none() {
-            let device_pixel_ratio = framebuffer_size.width as f32 /
-                builder.content_size().width;
-            self.init_output_document(api, DeviceUintSize::new(200, 200), device_pixel_ratio);
-        }
-
-        let info = LayoutPrimitiveInfo::new((100, 100).to(200, 200));
-        builder.push_stacking_context(
-            &info,
-            None,
-            TransformStyle::Flat,
-            MixBlendMode::Normal,
-            Vec::new(),
-            GlyphRasterSpace::Screen,
-        );
-
-        builder.push_image(
-            &info,
-            info.rect.size,
-            LayoutSize::zero(),
-            ImageRendering::Auto,
-            AlphaType::PremultipliedAlpha,
-            self.external_image_key.unwrap()
-        );
-
-        builder.pop_stacking_context();
-    }
-
-    fn get_image_handlers(
-        &mut self,
-        gl: &gl::Gl,
-    ) -> (Option<Box<webrender::ExternalImageHandler>>,
-          Option<Box<webrender::OutputImageHandler>>) {
-        let texture_id = gl.gen_textures(1)[0];
-
-        gl.bind_texture(gl::TEXTURE_2D, texture_id);
-        gl.tex_parameter_i(
-            gl::TEXTURE_2D,
-            gl::TEXTURE_MAG_FILTER,
-            gl::LINEAR as gl::GLint,
-        );
-        gl.tex_parameter_i(
-            gl::TEXTURE_2D,
-            gl::TEXTURE_MIN_FILTER,
-            gl::LINEAR as gl::GLint,
-        );
-        gl.tex_parameter_i(
-            gl::TEXTURE_2D,
-            gl::TEXTURE_WRAP_S,
-            gl::CLAMP_TO_EDGE as gl::GLint,
-        );
-        gl.tex_parameter_i(
-            gl::TEXTURE_2D,
-            gl::TEXTURE_WRAP_T,
-            gl::CLAMP_TO_EDGE as gl::GLint,
-        );
-        gl.tex_image_2d(
-            gl::TEXTURE_2D,
-            0,
-            gl::RGBA as gl::GLint,
-            100,
-            100,
-            0,
-            gl::BGRA,
-            gl::UNSIGNED_BYTE,
-            None,
-        );
-        gl.bind_texture(gl::TEXTURE_2D, 0);
-
-        (
-            Some(Box::new(ExternalHandler { texture_id })),
-            Some(Box::new(OutputHandler { texture_id }))
-        )
-    }
-}
-
-fn main() {
-    let mut app = App {
-        external_image_key: None,
-        output_document: None
-    };
-
-    boilerplate::main_wrapper(&mut app, None);
-}
deleted file mode 100644
--- a/gfx/webrender/examples/iframe.rs
+++ /dev/null
@@ -1,94 +0,0 @@
-/* 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/. */
-
-extern crate gleam;
-extern crate glutin;
-extern crate webrender;
-extern crate winit;
-
-#[path = "common/boilerplate.rs"]
-mod boilerplate;
-
-use boilerplate::{Example, HandyDandyRectBuilder};
-use webrender::api::*;
-
-// This example uses the push_iframe API to nest a second pipeline's displaylist
-// inside the root pipeline's display list. When it works, a green square is
-// shown. If it fails, a red square is shown.
-
-struct App {}
-
-impl Example for App {
-    fn render(
-        &mut self,
-        api: &RenderApi,
-        builder: &mut DisplayListBuilder,
-        _txn: &mut Transaction,
-        _framebuffer_size: DeviceUintSize,
-        pipeline_id: PipelineId,
-        document_id: DocumentId,
-    ) {
-        // All the sub_* things are for the nested pipeline
-        let sub_size = DeviceUintSize::new(100, 100);
-        let sub_bounds = (0, 0).to(sub_size.width as i32, sub_size.height as i32);
-
-        let sub_pipeline_id = PipelineId(pipeline_id.0, 42);
-        let mut sub_builder = DisplayListBuilder::new(sub_pipeline_id, sub_bounds.size);
-
-        let info = LayoutPrimitiveInfo::new(sub_bounds);
-        sub_builder.push_stacking_context(
-            &info,
-            None,
-            TransformStyle::Flat,
-            MixBlendMode::Normal,
-            Vec::new(),
-            GlyphRasterSpace::Screen,
-        );
-
-        // green rect visible == success
-        sub_builder.push_rect(&info, ColorF::new(0.0, 1.0, 0.0, 1.0));
-        sub_builder.pop_stacking_context();
-
-        let mut txn = Transaction::new();
-        txn.set_display_list(
-            Epoch(0),
-            None,
-            sub_bounds.size,
-            sub_builder.finalize(),
-            true,
-        );
-        api.send_transaction(document_id, txn);
-
-        let info = LayoutPrimitiveInfo::new(sub_bounds);
-        let reference_frame_id = builder.push_reference_frame(
-            &info,
-            Some(PropertyBinding::Binding(PropertyBindingKey::new(42), LayoutTransform::identity())),
-            None,
-        );
-        builder.push_clip_id(reference_frame_id);
-
-
-        // And this is for the root pipeline
-        builder.push_stacking_context(
-            &info,
-            None,
-            TransformStyle::Flat,
-            MixBlendMode::Normal,
-            Vec::new(),
-            GlyphRasterSpace::Screen,
-        );
-        // red rect under the iframe: if this is visible, things have gone wrong
-        builder.push_rect(&info, ColorF::new(1.0, 0.0, 0.0, 1.0));
-        builder.push_iframe(&info, sub_pipeline_id, false);
-        builder.pop_stacking_context();
-
-        builder.pop_clip_id();
-        builder.pop_reference_frame();
-    }
-}
-
-fn main() {
-    let mut app = App {};
-    boilerplate::main_wrapper(&mut app, None);
-}
deleted file mode 100644
--- a/gfx/webrender/examples/image_resize.rs
+++ /dev/null
@@ -1,124 +0,0 @@
-/* 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/. */
-
-extern crate gleam;
-extern crate glutin;
-extern crate webrender;
-extern crate winit;
-
-#[path = "common/boilerplate.rs"]
-mod boilerplate;
-#[path = "common/image_helper.rs"]
-mod image_helper;
-
-use boilerplate::{Example, HandyDandyRectBuilder};
-use webrender::api::*;
-
-struct App {
-    image_key: ImageKey,
-}
-
-impl Example for App {
-    fn render(
-        &mut self,
-        _api: &RenderApi,
-        builder: &mut DisplayListBuilder,
-        txn: &mut Transaction,
-        _framebuffer_size: DeviceUintSize,
-        _pipeline_id: PipelineId,
-        _document_id: DocumentId,
-    ) {
-        let (image_descriptor, image_data) = image_helper::make_checkerboard(32, 32);
-        txn.add_image(
-            self.image_key,
-            image_descriptor,
-            image_data,
-            None,
-        );
-
-        let bounds = (0, 0).to(512, 512);
-        let info = LayoutPrimitiveInfo::new(bounds);
-        builder.push_stacking_context(
-            &info,
-            None,
-            TransformStyle::Flat,
-            MixBlendMode::Normal,
-            Vec::new(),
-            GlyphRasterSpace::Screen,
-        );
-
-        let image_size = LayoutSize::new(100.0, 100.0);
-
-        let info = LayoutPrimitiveInfo::with_clip_rect(
-            LayoutRect::new(LayoutPoint::new(100.0, 100.0), image_size),
-            bounds,
-        );
-        builder.push_image(
-            &info,
-            image_size,
-            LayoutSize::zero(),
-            ImageRendering::Auto,
-            AlphaType::PremultipliedAlpha,
-            self.image_key,
-        );
-
-        let info = LayoutPrimitiveInfo::with_clip_rect(
-            LayoutRect::new(LayoutPoint::new(250.0, 100.0), image_size),
-            bounds,
-        );
-        builder.push_image(
-            &info,
-            image_size,
-            LayoutSize::zero(),
-            ImageRendering::Pixelated,
-            AlphaType::PremultipliedAlpha,
-            self.image_key,
-        );
-
-        builder.pop_stacking_context();
-    }
-
-    fn on_event(&mut self, event: winit::WindowEvent, api: &RenderApi, document_id: DocumentId) -> bool {
-        match event {
-            winit::WindowEvent::KeyboardInput {
-                input: winit::KeyboardInput {
-                    state: winit::ElementState::Pressed,
-                    virtual_keycode: Some(winit::VirtualKeyCode::Space),
-                    ..
-                },
-                ..
-            } => {
-                let mut image_data = Vec::new();
-                for y in 0 .. 64 {
-                    for x in 0 .. 64 {
-                        let r = 255 * ((y & 32) == 0) as u8;
-                        let g = 255 * ((x & 32) == 0) as u8;
-                        image_data.extend_from_slice(&[0, g, r, 0xff]);
-                    }
-                }
-
-                let mut txn = Transaction::new();
-                txn.update_image(
-                    self.image_key,
-                    ImageDescriptor::new(64, 64, ImageFormat::BGRA8, true, false),
-                    ImageData::new(image_data),
-                    None,
-                );
-                let mut txn = Transaction::new();
-                txn.generate_frame();
-                api.send_transaction(document_id, txn);
-            }
-            _ => {}
-        }
-
-        false
-    }
-}
-
-fn main() {
-    let mut app = App {
-        image_key: ImageKey(IdNamespace(0), 0),
-    };
-    boilerplate::main_wrapper(&mut app, None);
-}
deleted file mode 100644
--- a/gfx/webrender/examples/multiwindow.rs
+++ /dev/null
@@ -1,306 +0,0 @@
-/* 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/. */
-
-extern crate app_units;
-extern crate euclid;
-extern crate gleam;
-extern crate glutin;
-extern crate webrender;
-extern crate winit;
-
-use app_units::Au;
-use gleam::gl;
-use glutin::GlContext;
-use std::fs::File;
-use std::io::Read;
-use webrender::api::*;
-
-struct Notifier {
-    events_proxy: winit::EventsLoopProxy,
-}
-
-impl Notifier {
-    fn new(events_proxy: winit::EventsLoopProxy) -> Notifier {
-        Notifier { events_proxy }
-    }
-}
-
-impl RenderNotifier for Notifier {
-    fn clone(&self) -> Box<RenderNotifier> {
-        Box::new(Notifier {
-            events_proxy: self.events_proxy.clone(),
-        })
-    }
-
-    fn wake_up(&self) {
-        #[cfg(not(target_os = "android"))]
-        let _ = self.events_proxy.wakeup();
-    }
-
-    fn new_frame_ready(&self, _: DocumentId, _scrolled: bool, _composite_needed: bool) {
-        self.wake_up();
-    }
-}
-
-struct Window {
-    events_loop: winit::EventsLoop, //TODO: share events loop?
-    window: glutin::GlWindow,
-    renderer: webrender::Renderer,
-    name: &'static str,
-    pipeline_id: PipelineId,
-    document_id: DocumentId,
-    epoch: Epoch,
-    api: RenderApi,
-    font_instance_key: FontInstanceKey,
-}
-
-impl Window {
-    fn new(name: &'static str, clear_color: ColorF) -> Self {
-        let events_loop = winit::EventsLoop::new();
-        let context_builder = glutin::ContextBuilder::new()
-            .with_gl(glutin::GlRequest::GlThenGles {
-                opengl_version: (3, 2),
-                opengles_version: (3, 0),
-            });
-        let window_builder = winit::WindowBuilder::new()
-            .with_title(name)
-            .with_multitouch()
-            .with_dimensions(800, 600);
-        let window = glutin::GlWindow::new(window_builder, context_builder, &events_loop)
-            .unwrap();
-
-        unsafe {
-            window.make_current().ok();
-        }
-
-        let gl = match window.get_api() {
-            glutin::Api::OpenGl => unsafe {
-                gl::GlFns::load_with(|symbol| window.get_proc_address(symbol) as *const _)
-            },
-            glutin::Api::OpenGlEs => unsafe {
-                gl::GlesFns::load_with(|symbol| window.get_proc_address(symbol) as *const _)
-            },
-            glutin::Api::WebGl => unimplemented!(),
-        };
-
-        let device_pixel_ratio = window.hidpi_factor();
-
-        let opts = webrender::RendererOptions {
-            device_pixel_ratio,
-            clear_color: Some(clear_color),
-            ..webrender::RendererOptions::default()
-        };
-
-        let framebuffer_size = {
-            let (width, height) = window.get_inner_size().unwrap();
-            DeviceUintSize::new(width, height)
-        };
-        let notifier = Box::new(Notifier::new(events_loop.create_proxy()));
-        let (renderer, sender) = webrender::Renderer::new(gl.clone(), notifier, opts).unwrap();
-        let api = sender.create_api();
-        let document_id = api.add_document(framebuffer_size, 0);
-
-        let epoch = Epoch(0);
-        let pipeline_id = PipelineId(0, 0);
-        let mut txn = Transaction::new();
-
-        let font_key = api.generate_font_key();
-        let font_bytes = load_file("../wrench/reftests/text/FreeSans.ttf");
-        txn.add_raw_font(font_key, font_bytes, 0);
-
-        let font_instance_key = api.generate_font_instance_key();
-        txn.add_font_instance(font_instance_key, font_key, Au::from_px(32), None, None, Vec::new());
-
-        api.send_transaction(document_id, txn);
-
-        Window {
-            events_loop,
-            window,
-            renderer,
-            name,
-            epoch,
-            pipeline_id,
-            document_id,
-            api,
-            font_instance_key,
-        }
-    }
-
-    fn tick(&mut self) -> bool {
-        unsafe {
-            self.window.make_current().ok();
-        }
-        let mut do_exit = false;
-        let my_name = &self.name;
-        let renderer = &mut self.renderer;
-
-        self.events_loop.poll_events(|global_event| match global_event {
-            winit::Event::WindowEvent { event, .. } => match event {
-                winit::WindowEvent::CloseRequested |
-                winit::WindowEvent::KeyboardInput {
-                    input: winit::KeyboardInput {
-                        virtual_keycode: Some(winit::VirtualKeyCode::Escape),
-                        ..
-                    },
-                    ..
-                } => {
-                    do_exit = true
-                }
-                winit::WindowEvent::KeyboardInput {
-                    input: winit::KeyboardInput {
-                        state: winit::ElementState::Pressed,
-                        virtual_keycode: Some(winit::VirtualKeyCode::P),
-                        ..
-                    },
-                    ..
-                } => {
-                    println!("toggle flags {}", my_name);
-                    renderer.toggle_debug_flags(webrender::DebugFlags::PROFILER_DBG);
-                }
-                _ => {}
-            }
-            _ => {}
-        });
-        if do_exit {
-            return true
-        }
-
-        let framebuffer_size = {
-            let (width, height) = self.window.get_inner_size().unwrap();
-            DeviceUintSize::new(width, height)
-        };
-        let device_pixel_ratio = self.window.hidpi_factor();
-        let layout_size = framebuffer_size.to_f32() / euclid::TypedScale::new(device_pixel_ratio);
-        let mut txn = Transaction::new();
-        let mut builder = DisplayListBuilder::new(self.pipeline_id, layout_size);
-
-        let bounds = LayoutRect::new(LayoutPoint::zero(), builder.content_size());
-        let info = LayoutPrimitiveInfo::new(bounds);
-        builder.push_stacking_context(
-            &info,
-            None,
-            TransformStyle::Flat,
-            MixBlendMode::Normal,
-            Vec::new(),
-            GlyphRasterSpace::Screen,
-        );
-
-        let info = LayoutPrimitiveInfo::new(LayoutRect::new(
-            LayoutPoint::new(100.0, 100.0),
-            LayoutSize::new(100.0, 200.0)
-        ));
-        builder.push_rect(&info, ColorF::new(0.0, 1.0, 0.0, 1.0));
-
-        let text_bounds = LayoutRect::new(
-            LayoutPoint::new(100.0, 50.0),
-            LayoutSize::new(700.0, 200.0)
-        );
-        let glyphs = vec![
-            GlyphInstance {
-                index: 48,
-                point: LayoutPoint::new(100.0, 100.0),
-            },
-            GlyphInstance {
-                index: 68,
-                point: LayoutPoint::new(150.0, 100.0),
-            },
-            GlyphInstance {
-                index: 80,
-                point: LayoutPoint::new(200.0, 100.0),
-            },
-            GlyphInstance {
-                index: 82,
-                point: LayoutPoint::new(250.0, 100.0),
-            },
-            GlyphInstance {
-                index: 81,
-                point: LayoutPoint::new(300.0, 100.0),
-            },
-            GlyphInstance {
-                index: 3,
-                point: LayoutPoint::new(350.0, 100.0),
-            },
-            GlyphInstance {
-                index: 86,
-                point: LayoutPoint::new(400.0, 100.0),
-            },
-            GlyphInstance {
-                index: 79,
-                point: LayoutPoint::new(450.0, 100.0),
-            },
-            GlyphInstance {
-                index: 72,
-                point: LayoutPoint::new(500.0, 100.0),
-            },
-            GlyphInstance {
-                index: 83,
-                point: LayoutPoint::new(550.0, 100.0),
-            },
-            GlyphInstance {
-                index: 87,
-                point: LayoutPoint::new(600.0, 100.0),
-            },
-            GlyphInstance {
-                index: 17,
-                point: LayoutPoint::new(650.0, 100.0),
-            },
-        ];
-
-        let info = LayoutPrimitiveInfo::new(text_bounds);
-        builder.push_text(
-            &info,
-            &glyphs,
-            self.font_instance_key,
-            ColorF::new(1.0, 1.0, 0.0, 1.0),
-            None,
-        );
-
-        builder.pop_stacking_context();
-
-        txn.set_display_list(
-            self.epoch,
-            None,
-            layout_size,
-            builder.finalize(),
-            true,
-        );
-        txn.set_root_pipeline(self.pipeline_id);
-        txn.generate_frame();
-        self.api.send_transaction(self.document_id, txn);
-
-        renderer.update();
-        renderer.render(framebuffer_size).unwrap();
-        self.window.swap_buffers().ok();
-
-        false
-    }
-
-    fn deinit(self) {
-        self.renderer.deinit();
-    }
-}
-
-fn main() {
-    let mut win1 = Window::new("window1", ColorF::new(0.3, 0.0, 0.0, 1.0));
-    let mut win2 = Window::new("window2", ColorF::new(0.0, 0.3, 0.0, 1.0));
-
-    loop {
-        if win1.tick() {
-            break;
-        }
-        if win2.tick() {
-            break;
-        }
-    }
-
-    win1.deinit();
-    win2.deinit();
-}
-
-fn load_file(name: &str) -> Vec<u8> {
-    let mut file = File::open(name).unwrap();
-    let mut buffer = vec![];
-    file.read_to_end(&mut buffer).unwrap();
-    buffer
-}
deleted file mode 100644
--- a/gfx/webrender/examples/scrolling.rs
+++ /dev/null
@@ -1,210 +0,0 @@
-/* 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/. */
-
-extern crate euclid;
-extern crate gleam;
-extern crate glutin;
-extern crate webrender;
-extern crate winit;
-
-#[path = "common/boilerplate.rs"]
-mod boilerplate;
-
-use boilerplate::{Example, HandyDandyRectBuilder};
-use euclid::SideOffsets2D;
-use webrender::api::*;
-
-struct App {
-    cursor_position: WorldPoint,
-}
-
-impl Example for App {
-    fn render(
-        &mut self,
-        _api: &RenderApi,
-        builder: &mut DisplayListBuilder,
-        _txn: &mut Transaction,
-        _framebuffer_size: DeviceUintSize,
-        _pipeline_id: PipelineId,
-        _document_id: DocumentId,
-    ) {
-        let info = LayoutPrimitiveInfo::new(
-            LayoutRect::new(LayoutPoint::zero(), builder.content_size())
-        );
-        builder.push_stacking_context(
-            &info,
-            None,
-            TransformStyle::Flat,
-            MixBlendMode::Normal,
-            Vec::new(),
-            GlyphRasterSpace::Screen,
-        );
-
-        if true {
-            // scrolling and clips stuff
-            // let's make a scrollbox
-            let scrollbox = (0, 0).to(300, 400);
-            builder.push_stacking_context(
-                &LayoutPrimitiveInfo::new((10, 10).by(0, 0)),
-                None,
-                TransformStyle::Flat,
-                MixBlendMode::Normal,
-                Vec::new(),
-                GlyphRasterSpace::Screen,
-            );
-            // set the scrolling clip
-            let clip_id = builder.define_scroll_frame(
-                None,
-                (0, 0).by(1000, 1000),
-                scrollbox,
-                vec![],
-                None,
-                ScrollSensitivity::ScriptAndInputEvents,
-            );
-            builder.push_clip_id(clip_id);
-
-            // now put some content into it.
-            // start with a white background
-            let mut info = LayoutPrimitiveInfo::new((0, 0).to(1000, 1000));
-            info.tag = Some((0, 1));
-            builder.push_rect(&info, ColorF::new(1.0, 1.0, 1.0, 1.0));
-
-            // let's make a 50x50 blue square as a visual reference
-            let mut info = LayoutPrimitiveInfo::new((0, 0).to(50, 50));
-            info.tag = Some((0, 2));
-            builder.push_rect(&info, ColorF::new(0.0, 0.0, 1.0, 1.0));
-
-            // and a 50x50 green square next to it with an offset clip
-            // to see what that looks like
-            let mut info =
-                LayoutPrimitiveInfo::with_clip_rect((50, 0).to(100, 50), (60, 10).to(110, 60));
-            info.tag = Some((0, 3));
-            builder.push_rect(&info, ColorF::new(0.0, 1.0, 0.0, 1.0));
-
-            // Below the above rectangles, set up a nested scrollbox. It's still in
-            // the same stacking context, so note that the rects passed in need to
-            // be relative to the stacking context.
-            let nested_clip_id = builder.define_scroll_frame(
-                None,
-                (0, 100).to(300, 1000),
-                (0, 100).to(200, 300),
-                vec![],
-                None,
-                ScrollSensitivity::ScriptAndInputEvents,
-            );
-            builder.push_clip_id(nested_clip_id);
-
-            // give it a giant gray background just to distinguish it and to easily
-            // visually identify the nested scrollbox
-            let mut info = LayoutPrimitiveInfo::new((-1000, -1000).to(5000, 5000));
-            info.tag = Some((0, 4));
-            builder.push_rect(&info, ColorF::new(0.5, 0.5, 0.5, 1.0));
-
-            // add a teal square to visualize the scrolling/clipping behaviour
-            // as you scroll the nested scrollbox
-            let mut info = LayoutPrimitiveInfo::new((0, 200).to(50, 250));
-            info.tag = Some((0, 5));
-            builder.push_rect(&info, ColorF::new(0.0, 1.0, 1.0, 1.0));
-
-            // Add a sticky frame. It will "stick" twice while scrolling, once
-            // at a margin of 10px from the bottom, for 40 pixels of scrolling,
-            // and once at a margin of 10px from the top, for 60 pixels of
-            // scrolling.
-            let sticky_id = builder.define_sticky_frame(
-                (50, 350).by(50, 50),
-                SideOffsets2D::new(Some(10.0), None, Some(10.0), None),
-                StickyOffsetBounds::new(-40.0, 60.0),
-                StickyOffsetBounds::new(0.0, 0.0),
-                LayoutVector2D::new(0.0, 0.0)
-            );
-
-            builder.push_clip_id(sticky_id);
-            let mut info = LayoutPrimitiveInfo::new((50, 350).by(50, 50));
-            info.tag = Some((0, 6));
-            builder.push_rect(&info, ColorF::new(0.5, 0.5, 1.0, 1.0));
-            builder.pop_clip_id(); // sticky_id
-
-            // just for good measure add another teal square further down and to
-            // the right, which can be scrolled into view by the user
-            let mut info = LayoutPrimitiveInfo::new((250, 350).to(300, 400));
-            info.tag = Some((0, 7));
-            builder.push_rect(&info, ColorF::new(0.0, 1.0, 1.0, 1.0));
-
-            builder.pop_clip_id(); // nested_clip_id
-
-            builder.pop_clip_id(); // clip_id
-            builder.pop_stacking_context();
-        }
-
-        builder.pop_stacking_context();
-    }
-
-    fn on_event(&mut self, event: winit::WindowEvent, api: &RenderApi, document_id: DocumentId) -> bool {
-        let mut txn = Transaction::new();
-        match event {
-            winit::WindowEvent::KeyboardInput {
-                input: winit::KeyboardInput {
-                    state: winit::ElementState::Pressed,
-                    virtual_keycode: Some(key),
-                    ..
-                },
-                ..
-            } => {
-                let offset = match key {
-                    winit::VirtualKeyCode::Down => (0.0, -10.0),
-                    winit::VirtualKeyCode::Up => (0.0, 10.0),
-                    winit::VirtualKeyCode::Right => (-10.0, 0.0),
-                    winit::VirtualKeyCode::Left => (10.0, 0.0),
-                    _ => return false,
-                };
-
-                txn.scroll(
-                    ScrollLocation::Delta(LayoutVector2D::new(offset.0, offset.1)),
-                    self.cursor_position,
-                );
-            }
-            winit::WindowEvent::CursorMoved { position: (x, y), .. } => {
-                self.cursor_position = WorldPoint::new(x as f32, y as f32);
-            }
-            winit::WindowEvent::MouseWheel { delta, .. } => {
-                const LINE_HEIGHT: f32 = 38.0;
-                let (dx, dy) = match delta {
-                    winit::MouseScrollDelta::LineDelta(dx, dy) => (dx, dy * LINE_HEIGHT),
-                    winit::MouseScrollDelta::PixelDelta(dx, dy) => (dx, dy),
-                };
-
-                txn.scroll(
-                    ScrollLocation::Delta(LayoutVector2D::new(dx, dy)),
-                    self.cursor_position,
-                );
-            }
-            winit::WindowEvent::MouseInput { .. } => {
-                let results = api.hit_test(
-                    document_id,
-                    None,
-                    self.cursor_position,
-                    HitTestFlags::FIND_ALL
-                );
-
-                println!("Hit test results:");
-                for item in &results.items {
-                    println!("  • {:?}", item);
-                }
-                println!("");
-            }
-            _ => (),
-        }
-
-        api.send_transaction(document_id, txn);
-
-        false
-    }
-}
-
-fn main() {
-    let mut app = App {
-        cursor_position: WorldPoint::zero(),
-    };
-    boilerplate::main_wrapper(&mut app, None);
-}
deleted file mode 100644
--- a/gfx/webrender/examples/texture_cache_stress.rs
+++ /dev/null
@@ -1,312 +0,0 @@
-/* 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/. */
-
-extern crate gleam;
-extern crate glutin;
-extern crate webrender;
-extern crate winit;
-
-#[path = "common/boilerplate.rs"]
-mod boilerplate;
-
-use boilerplate::{Example, HandyDandyRectBuilder};
-use gleam::gl;
-use std::mem;
-use webrender::api::*;
-
-struct ImageGenerator {
-    patterns: [[u8; 3]; 6],
-    next_pattern: usize,
-    current_image: Vec<u8>,
-}
-
-impl ImageGenerator {
-    fn new() -> Self {
-        ImageGenerator {
-            next_pattern: 0,
-            patterns: [
-                [1, 0, 0],
-                [0, 1, 0],
-                [0, 0, 1],
-                [1, 1, 0],
-                [0, 1, 1],
-                [1, 0, 1],
-            ],
-            current_image: Vec::new(),
-        }
-    }
-
-    fn generate_image(&mut self, size: u32) {
-        let pattern = &self.patterns[self.next_pattern];
-        self.current_image.clear();
-        for y in 0 .. size {
-            for x in 0 .. size {
-                let lum = 255 * (1 - (((x & 8) == 0) ^ ((y & 8) == 0)) as u8);
-                self.current_image.extend_from_slice(&[
-                    lum * pattern[0],
-                    lum * pattern[1],
-                    lum * pattern[2],
-                    0xff,
-                ]);
-            }
-        }
-
-        self.next_pattern = (self.next_pattern + 1) % self.patterns.len();
-    }
-
-    fn take(&mut self) -> Vec<u8> {
-        mem::replace(&mut self.current_image, Vec::new())
-    }
-}
-
-impl webrender::ExternalImageHandler for ImageGenerator {
-    fn lock(&mut self, _key: ExternalImageId, channel_index: u8) -> webrender::ExternalImage {
-        self.generate_image(channel_index as u32);
-        webrender::ExternalImage {
-            uv: TexelRect::new(0.0, 0.0, 1.0, 1.0),
-            source: webrender::ExternalImageSource::RawData(&self.current_image),
-        }
-    }
-    fn unlock(&mut self, _key: ExternalImageId, _channel_index: u8) {}
-}
-
-struct App {
-    stress_keys: Vec<ImageKey>,
-    image_key: Option<ImageKey>,
-    image_generator: ImageGenerator,
-    swap_keys: Vec<ImageKey>,
-    swap_index: usize,
-}
-
-impl Example for App {
-    fn render(
-        &mut self,
-        api: &RenderApi,
-        builder: &mut DisplayListBuilder,
-        txn: &mut Transaction,
-        _framebuffer_size: DeviceUintSize,
-        _pipeline_id: PipelineId,
-        _document_id: DocumentId,
-    ) {
-        let bounds = (0, 0).to(512, 512);
-        let info = LayoutPrimitiveInfo::new(bounds);
-        builder.push_stacking_context(
-            &info,
-            None,
-            TransformStyle::Flat,
-            MixBlendMode::Normal,
-            Vec::new(),
-            GlyphRasterSpace::Screen,
-        );
-
-        let x0 = 50.0;
-        let y0 = 50.0;
-        let image_size = LayoutSize::new(4.0, 4.0);
-
-        if self.swap_keys.is_empty() {
-            let key0 = api.generate_image_key();
-            let key1 = api.generate_image_key();
-
-            self.image_generator.generate_image(128);
-            txn.add_image(
-                key0,
-                ImageDescriptor::new(128, 128, ImageFormat::BGRA8, true, false),
-                ImageData::new(self.image_generator.take()),
-                None,
-            );
-
-            self.image_generator.generate_image(128);
-            txn.add_image(
-                key1,
-                ImageDescriptor::new(128, 128, ImageFormat::BGRA8, true, false),
-                ImageData::new(self.image_generator.take()),
-                None,
-            );
-
-            self.swap_keys.push(key0);
-            self.swap_keys.push(key1);
-        }
-
-        for (i, key) in self.stress_keys.iter().enumerate() {
-            let x = (i % 128) as f32;
-            let y = (i / 128) as f32;
-            let info = LayoutPrimitiveInfo::with_clip_rect(
-                LayoutRect::new(
-                    LayoutPoint::new(x0 + image_size.width * x, y0 + image_size.height * y),
-                    image_size,
-                ),
-                bounds,
-            );
-
-            builder.push_image(
-                &info,
-                image_size,
-                LayoutSize::zero(),
-                ImageRendering::Auto,
-                AlphaType::PremultipliedAlpha,
-                *key,
-            );
-        }
-
-        if let Some(image_key) = self.image_key {
-            let image_size = LayoutSize::new(100.0, 100.0);
-            let info = LayoutPrimitiveInfo::with_clip_rect(
-                LayoutRect::new(LayoutPoint::new(100.0, 100.0), image_size),
-                bounds,
-            );
-            builder.push_image(
-                &info,
-                image_size,
-                LayoutSize::zero(),
-                ImageRendering::Auto,
-                AlphaType::PremultipliedAlpha,
-                image_key,
-            );
-        }
-
-        let swap_key = self.swap_keys[self.swap_index];
-        let image_size = LayoutSize::new(64.0, 64.0);
-        let info = LayoutPrimitiveInfo::with_clip_rect(
-            LayoutRect::new(LayoutPoint::new(100.0, 400.0), image_size),
-            bounds,
-        );
-        builder.push_image(
-            &info,
-            image_size,
-            LayoutSize::zero(),
-            ImageRendering::Auto,
-            AlphaType::PremultipliedAlpha,
-            swap_key,
-        );
-        self.swap_index = 1 - self.swap_index;
-
-        builder.pop_stacking_context();
-    }
-
-    fn on_event(
-        &mut self,
-        event: winit::WindowEvent,
-        api: &RenderApi,
-        _document_id: DocumentId,
-    ) -> bool {
-        match event {
-            winit::WindowEvent::KeyboardInput {
-                input: winit::KeyboardInput {
-                    state: winit::ElementState::Pressed,
-                    virtual_keycode: Some(key),
-                    ..
-                },
-                ..
-            } => {
-                let mut txn = Transaction::new();
-
-                match key {
-                    winit::VirtualKeyCode::S => {
-                        self.stress_keys.clear();
-
-                        for _ in 0 .. 16 {
-                            for _ in 0 .. 16 {
-                                let size = 4;
-
-                                let image_key = api.generate_image_key();
-
-                                self.image_generator.generate_image(size);
-
-                                txn.add_image(
-                                    image_key,
-                                    ImageDescriptor::new(size, size, ImageFormat::BGRA8, true, false),
-                                    ImageData::new(self.image_generator.take()),
-                                    None,
-                                );
-
-                                self.stress_keys.push(image_key);
-                            }
-                        }
-                    }
-                    winit::VirtualKeyCode::D => if let Some(image_key) = self.image_key.take() {
-                        txn.delete_image(image_key);
-                    },
-                    winit::VirtualKeyCode::U => if let Some(image_key) = self.image_key {
-                        let size = 128;
-                        self.image_generator.generate_image(size);
-
-                        txn.update_image(
-                            image_key,
-                            ImageDescriptor::new(size, size, ImageFormat::BGRA8, true, false),
-                            ImageData::new(self.image_generator.take()),
-                            None,
-                        );
-                    },
-                    winit::VirtualKeyCode::E => {
-                        if let Some(image_key) = self.image_key.take() {
-                            txn.delete_image(image_key);
-                        }
-
-                        let size = 32;
-                        let image_key = api.generate_image_key();
-
-                        let image_data = ExternalImageData {
-                            id: ExternalImageId(0),
-                            channel_index: size as u8,
-                            image_type: ExternalImageType::Buffer,
-                        };
-
-                        txn.add_image(
-                            image_key,
-                            ImageDescriptor::new(size, size, ImageFormat::BGRA8, true, false),
-                            ImageData::External(image_data),
-                            None,
-                        );
-
-                        self.image_key = Some(image_key);
-                    }
-                    winit::VirtualKeyCode::R => {
-                        if let Some(image_key) = self.image_key.take() {
-                            txn.delete_image(image_key);
-                        }
-
-                        let image_key = api.generate_image_key();
-                        let size = 32;
-                        self.image_generator.generate_image(size);
-
-                        txn.add_image(
-                            image_key,
-                            ImageDescriptor::new(size, size, ImageFormat::BGRA8, true, false),
-                            ImageData::new(self.image_generator.take()),
-                            None,
-                        );
-
-                        self.image_key = Some(image_key);
-                    }
-                    _ => {}
-                }
-
-                api.update_resources(txn.resource_updates);
-                return true;
-            }
-            _ => {}
-        }
-
-        false
-    }
-
-    fn get_image_handlers(
-        &mut self,
-        _gl: &gl::Gl,
-    ) -> (Option<Box<webrender::ExternalImageHandler>>,
-          Option<Box<webrender::OutputImageHandler>>) {
-        (Some(Box::new(ImageGenerator::new())), None)
-    }
-}
-
-fn main() {
-    let mut app = App {
-        image_key: None,
-        stress_keys: Vec::new(),
-        image_generator: ImageGenerator::new(),
-        swap_keys: Vec::new(),
-        swap_index: 0,
-    };
-    boilerplate::main_wrapper(&mut app, None);
-}
deleted file mode 100644
--- a/gfx/webrender/examples/yuv.rs
+++ /dev/null
@@ -1,198 +0,0 @@
-/* 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/. */
-
-extern crate gleam;
-extern crate glutin;
-extern crate webrender;
-extern crate winit;
-
-#[path = "common/boilerplate.rs"]
-mod boilerplate;
-
-use boilerplate::Example;
-use gleam::gl;
-use webrender::api::*;
-
-fn init_gl_texture(
-    id: gl::GLuint,
-    internal: gl::GLenum,
-    external: gl::GLenum,
-    bytes: &[u8],
-    gl: &gl::Gl,
-) {
-    gl.bind_texture(gl::TEXTURE_2D, id);
-    gl.tex_parameter_i(gl::TEXTURE_2D, gl::TEXTURE_MAG_FILTER, gl::LINEAR as gl::GLint);
-    gl.tex_parameter_i(gl::TEXTURE_2D, gl::TEXTURE_MIN_FILTER, gl::LINEAR as gl::GLint);
-    gl.tex_parameter_i(gl::TEXTURE_2D, gl::TEXTURE_WRAP_S, gl::CLAMP_TO_EDGE as gl::GLint);
-    gl.tex_parameter_i(gl::TEXTURE_2D, gl::TEXTURE_WRAP_T, gl::CLAMP_TO_EDGE as gl::GLint);
-    gl.tex_image_2d(
-        gl::TEXTURE_2D,
-        0,
-        internal as gl::GLint,
-        100,
-        100,
-        0,
-        external,
-        gl::UNSIGNED_BYTE,
-        Some(bytes),
-    );
-    gl.bind_texture(gl::TEXTURE_2D, 0);
-}
-
-struct YuvImageProvider {
-    texture_ids: Vec<gl::GLuint>,
-}
-
-impl YuvImageProvider {
-    fn new(gl: &gl::Gl) -> Self {
-        let texture_ids = gl.gen_textures(4);
-
-        init_gl_texture(texture_ids[0], gl::RED, gl::RED, &[127; 100 * 100], gl);
-        init_gl_texture(texture_ids[1], gl::RG8, gl::RG, &[0; 100 * 100 * 2], gl);
-        init_gl_texture(texture_ids[2], gl::RED, gl::RED, &[127; 100 * 100], gl);
-        init_gl_texture(texture_ids[3], gl::RED, gl::RED, &[127; 100 * 100], gl);
-
-        YuvImageProvider {
-            texture_ids
-        }
-    }
-}
-
-impl webrender::ExternalImageHandler for YuvImageProvider {
-    fn lock(&mut self, key: ExternalImageId, _channel_index: u8) -> webrender::ExternalImage {
-        let id = self.texture_ids[key.0 as usize];
-        webrender::ExternalImage {
-            uv: TexelRect::new(0.0, 0.0, 1.0, 1.0),
-            source: webrender::ExternalImageSource::NativeTexture(id),
-        }
-    }
-    fn unlock(&mut self, _key: ExternalImageId, _channel_index: u8) {
-    }
-}
-
-struct App {
-}
-
-impl Example for App {
-    fn render(
-        &mut self,
-        api: &RenderApi,
-        builder: &mut DisplayListBuilder,
-        txn: &mut Transaction,
-        _framebuffer_size: DeviceUintSize,
-        _pipeline_id: PipelineId,
-        _document_id: DocumentId,
-    ) {
-        let bounds = LayoutRect::new(LayoutPoint::zero(), builder.content_size());
-        let info = LayoutPrimitiveInfo::new(bounds);
-        builder.push_stacking_context(
-            &info,
-            None,
-            TransformStyle::Flat,
-            MixBlendMode::Normal,
-            Vec::new(),
-            GlyphRasterSpace::Screen,
-        );
-
-        let yuv_chanel1 = api.generate_image_key();
-        let yuv_chanel2 = api.generate_image_key();
-        let yuv_chanel2_1 = api.generate_image_key();
-        let yuv_chanel3 = api.generate_image_key();
-        txn.add_image(
-            yuv_chanel1,
-            ImageDescriptor::new(100, 100, ImageFormat::R8, true, false),
-            ImageData::External(ExternalImageData {
-                id: ExternalImageId(0),
-                channel_index: 0,
-                image_type: ExternalImageType::TextureHandle(
-                    TextureTarget::Default,
-                ),
-            }),
-            None,
-        );
-        txn.add_image(
-            yuv_chanel2,
-            ImageDescriptor::new(100, 100, ImageFormat::RG8, true, false),
-            ImageData::External(ExternalImageData {
-                id: ExternalImageId(1),
-                channel_index: 0,
-                image_type: ExternalImageType::TextureHandle(
-                    TextureTarget::Default,
-                ),
-            }),
-            None,
-        );
-        txn.add_image(
-            yuv_chanel2_1,
-            ImageDescriptor::new(100, 100, ImageFormat::R8, true, false),
-            ImageData::External(ExternalImageData {
-                id: ExternalImageId(2),
-                channel_index: 0,
-                image_type: ExternalImageType::TextureHandle(
-                    TextureTarget::Default,
-                ),
-            }),
-            None,
-        );
-        txn.add_image(
-            yuv_chanel3,
-            ImageDescriptor::new(100, 100, ImageFormat::R8, true, false),
-            ImageData::External(ExternalImageData {
-                id: ExternalImageId(3),
-                channel_index: 0,
-                image_type: ExternalImageType::TextureHandle(
-                    TextureTarget::Default,
-                ),
-            }),
-            None,
-        );
-
-        let info = LayoutPrimitiveInfo::with_clip_rect(
-            LayoutRect::new(LayoutPoint::new(100.0, 0.0), LayoutSize::new(100.0, 100.0)),
-            bounds,
-        );
-        builder.push_yuv_image(
-            &info,
-            YuvData::NV12(yuv_chanel1, yuv_chanel2),
-            YuvColorSpace::Rec601,
-            ImageRendering::Auto,
-        );
-
-        let info = LayoutPrimitiveInfo::with_clip_rect(
-            LayoutRect::new(LayoutPoint::new(300.0, 0.0), LayoutSize::new(100.0, 100.0)),
-            bounds,
-        );
-        builder.push_yuv_image(
-            &info,
-            YuvData::PlanarYCbCr(yuv_chanel1, yuv_chanel2_1, yuv_chanel3),
-            YuvColorSpace::Rec601,
-            ImageRendering::Auto,
-        );
-
-        builder.pop_stacking_context();
-    }
-
-    fn on_event(
-        &mut self,
-        _event: winit::WindowEvent,
-        _api: &RenderApi,
-        _document_id: DocumentId,
-    ) -> bool {
-        false
-    }
-
-    fn get_image_handlers(
-        &mut self,
-        gl: &gl::Gl,
-    ) -> (Option<Box<webrender::ExternalImageHandler>>,
-          Option<Box<webrender::OutputImageHandler>>) {
-        (Some(Box::new(YuvImageProvider::new(gl))), None)
-    }
-}
-
-fn main() {
-    let mut app = App {
-    };
-    boilerplate::main_wrapper(&mut app, None);
-}
--- a/gfx/webrender/res/ps_text_run.glsl
+++ b/gfx/webrender/res/ps_text_run.glsl
@@ -17,44 +17,52 @@ varying vec4 vUvClip;
 
 VertexInfo write_text_vertex(vec2 clamped_local_pos,
                              RectWithSize local_clip_rect,
                              float z,
                              ClipScrollNode scroll_node,
                              PictureTask task,
                              RectWithSize snap_rect,
                              vec2 snap_bias) {
-#if defined(WR_FEATURE_GLYPH_TRANSFORM) || !defined(WR_FEATURE_TRANSFORM)
     // Ensure the transform does not contain a subpixel translation to ensure
     // that glyph snapping is stable for equivalent glyph subpixel positions.
-    scroll_node.transform[3].xy = floor(scroll_node.transform[3].xy + 0.5);
+#if defined(WR_FEATURE_GLYPH_TRANSFORM)
+    bool remove_subpx_offset = true;
+#else
+    bool remove_subpx_offset = scroll_node.is_axis_aligned;
 #endif
 
+    if (remove_subpx_offset) {
+        scroll_node.transform[3].xy = floor(scroll_node.transform[3].xy + 0.5);
+    }
+
     // Transform the current vertex to world space.
     vec4 world_pos = scroll_node.transform * vec4(clamped_local_pos, 0.0, 1.0);
 
     // Convert the world positions to device pixel space.
     vec2 device_pos = world_pos.xy / world_pos.w * uDevicePixelRatio;
 
     // Apply offsets for the render task to get correct screen location.
     vec2 final_pos = device_pos -
                      task.content_origin +
                      task.common_data.task_rect.p0;
 
 #ifdef WR_FEATURE_GLYPH_TRANSFORM
     // For transformed subpixels, we just need to align the glyph origin to a device pixel.
     final_pos += floor(snap_rect.p0 + snap_bias) - snap_rect.p0;
-#elif !defined(WR_FEATURE_TRANSFORM)
+#else
     // Compute the snapping offset only if the scroll node transform is axis-aligned.
-    final_pos += compute_snap_offset(
-        clamped_local_pos,
-        scroll_node.transform,
-        snap_rect,
-        snap_bias
-    );
+    if (scroll_node.is_axis_aligned) {
+        final_pos += compute_snap_offset(
+            clamped_local_pos,
+            scroll_node.transform,
+            snap_rect,
+            snap_bias
+        );
+    }
 #endif
 
     gl_Position = uTransform * vec4(final_pos, z, 1.0);
 
     VertexInfo vi = VertexInfo(
         clamped_local_pos,
         device_pos,
         world_pos.w,
--- a/gfx/webrender/src/batch.rs
+++ b/gfx/webrender/src/batch.rs
@@ -33,23 +33,16 @@ use util::{MatrixHelpers, TransformedRec
 
 // Special sentinel value recognized by the shader. It is considered to be
 // a dummy task that doesn't mask out anything.
 const OPAQUE_TASK_ADDRESS: RenderTaskAddress = RenderTaskAddress(0x7fff);
 
 #[derive(Copy, Clone, PartialEq, Eq, Hash, Debug)]
 #[cfg_attr(feature = "capture", derive(Serialize))]
 #[cfg_attr(feature = "replay", derive(Deserialize))]
-pub enum TransformBatchKind {
-    TextRun(GlyphFormat),
-}
-
-#[derive(Copy, Clone, PartialEq, Eq, Hash, Debug)]
-#[cfg_attr(feature = "capture", derive(Serialize))]
-#[cfg_attr(feature = "replay", derive(Deserialize))]
 pub enum BrushBatchKind {
     Solid,
     Image(ImageBufferKind),
     Blend,
     MixBlend {
         task_id: RenderTaskId,
         source_id: RenderTaskId,
         backdrop_id: RenderTaskId,
@@ -59,17 +52,17 @@ pub enum BrushBatchKind {
     LinearGradient,
 }
 
 #[derive(Copy, Clone, PartialEq, Eq, Hash, Debug)]
 #[cfg_attr(feature = "capture", derive(Serialize))]
 #[cfg_attr(feature = "replay", derive(Deserialize))]
 pub enum BatchKind {
     SplitComposite,
-    Transformable(TransformedRectKind, TransformBatchKind),
+    TextRun(GlyphFormat),
     Brush(BrushBatchKind),
 }
 
 /// Optional textures that can be used as a source in the shaders.
 /// Textures that are not used by the batch are equal to TextureId::invalid().
 #[derive(Copy, Clone, Debug)]
 #[cfg_attr(feature = "capture", derive(Serialize))]
 #[cfg_attr(feature = "replay", derive(Deserialize))]
@@ -1096,17 +1089,17 @@ impl AlphaBatchBuilder {
                 }
             }
             PrimitiveKind::TextRun => {
                 let text_cpu =
                     &ctx.prim_store.cpu_text_runs[prim_metadata.cpu_prim_index.0];
 
                 let font = text_cpu.get_font(
                     ctx.device_pixel_scale,
-                    Some(scroll_node.transform),
+                    scroll_node.transform,
                 );
                 let subpx_dir = font.get_subpx_dir();
 
                 let glyph_fetch_buffer = &mut self.glyph_fetch_buffer;
                 let batch_list = &mut self.batch_list;
 
                 ctx.resource_cache.fetch_glyphs(
                     font,
@@ -1126,20 +1119,17 @@ impl AlphaBatchBuilder {
                         let textures = BatchTextures {
                             colors: [
                                 texture_id,
                                 SourceTexture::Invalid,
                                 SourceTexture::Invalid,
                             ],
                         };
 
-                        let kind = BatchKind::Transformable(
-                            transform_kind,
-                            TransformBatchKind::TextRun(glyph_format),
-                        );
+                        let kind = BatchKind::TextRun(glyph_format);
 
                         let (blend_mode, color_mode) = match glyph_format {
                             GlyphFormat::Subpixel |
                             GlyphFormat::TransformedSubpixel => {
                                 if text_cpu.font.bg_color.a != 0 {
                                     (
                                         BlendMode::SubpixelWithBgColor,
                                         ShaderColorMode::FromRenderPassMode,
--- a/gfx/webrender/src/display_list_flattener.rs
+++ b/gfx/webrender/src/display_list_flattener.rs
@@ -8,17 +8,16 @@ use api::{ClipId, ColorF, ComplexClipReg
 use api::{DevicePixelScale, DeviceUintRect, DisplayItemRef, Epoch, ExtendMode, ExternalScrollId};
 use api::{FilterOp, FontInstanceKey, GlyphInstance, GlyphOptions, GlyphRasterSpace, GradientStop};
 use api::{IframeDisplayItem, ImageKey, ImageRendering, ItemRange, LayoutPoint};
 use api::{LayoutPrimitiveInfo, LayoutRect, LayoutSize, LayoutTransform, LayoutVector2D};
 use api::{LineOrientation, LineStyle, LocalClip, NinePatchBorderSource, PipelineId};
 use api::{PropertyBinding, ReferenceFrame, RepeatMode, ScrollFrameDisplayItem, ScrollSensitivity};
 use api::{Shadow, SpecificDisplayItem, StackingContext, StickyFrameDisplayItem, TexelRect};
 use api::{TransformStyle, YuvColorSpace, YuvData};
-use app_units::Au;
 use clip::{ClipRegion, ClipSource, ClipSources, ClipStore};
 use clip_scroll_node::{ClipScrollNode, NodeType, StickyFrameInfo};
 use clip_scroll_tree::{ClipChainIndex, ClipScrollNodeIndex, ClipScrollTree};
 use euclid::{SideOffsets2D, vec2};
 use frame_builder::{FrameBuilder, FrameBuilderConfig};
 use glyph_rasterizer::FontInstance;
 use gpu_cache::GpuCacheHandle;
 use gpu_types::BrushFlags;
@@ -1884,26 +1883,16 @@ impl<'a> DisplayListFlattener<'a> {
                 }
             };
 
             // Trivial early out checks
             if font_instance.size.0 <= 0 {
                 return;
             }
 
-            // Sanity check - anything with glyphs bigger than this
-            // is probably going to consume too much memory to render
-            // efficiently anyway. This is specifically to work around
-            // the font_advance.html reftest, which creates a very large
-            // font as a crash test - the rendering is also ignored
-            // by the azure renderer.
-            if font_instance.size >= Au::from_px(4096) {
-                return;
-            }
-
             // TODO(gw): Use a proper algorithm to select
             // whether this item should be rendered with
             // subpixel AA!
             let mut render_mode = self.config
                 .default_font_render_mode
                 .limit_by(font_instance.render_mode);
             let mut flags = font_instance.flags;
             if let Some(options) = glyph_options {
--- a/gfx/webrender/src/glyph_rasterizer/mod.rs
+++ b/gfx/webrender/src/glyph_rasterizer/mod.rs
@@ -154,16 +154,18 @@ impl FontTransform {
 }
 
 impl<'a> From<&'a LayoutToWorldTransform> for FontTransform {
     fn from(xform: &'a LayoutToWorldTransform) -> Self {
         FontTransform::new(xform.m11, xform.m21, xform.m12, xform.m22)
     }
 }
 
+pub const FONT_SIZE_LIMIT: f64 = 1024.0;
+
 #[derive(Clone, Hash, PartialEq, Eq, Debug, Ord, PartialOrd)]
 #[cfg_attr(feature = "capture", derive(Serialize))]
 #[cfg_attr(feature = "replay", derive(Deserialize))]
 pub struct FontInstance {
     pub font_key: FontKey,
     // The font size is in *device* pixels, not logical pixels.
     // It is stored as an Au since we need sub-pixel sizes, but
     // can't store as a f32 due to use of this type as a hash key.
@@ -265,16 +267,33 @@ impl FontInstance {
             if bold_offset < 1.0 {
                 bold_offset = 0.25 + 0.75 * bold_offset;
             }
             (bold_offset * x_scale).max(1.0).round() as usize
         } else {
             0
         }
     }
+
+    pub fn oversized_scale_factor(&self, x_scale: f64, y_scale: f64) -> f64 {
+        // If the scaled size is over the limit, then it will need to
+        // be scaled up from the size limit to the scaled size.
+        // However, this should only occur when the font isn't using any
+        // features that would tie it to device space, like transforms,
+        // subpixel AA, or subpixel positioning.
+        let max_size = self.size.to_f64_px() * x_scale.max(y_scale);
+        if max_size > FONT_SIZE_LIMIT &&
+           self.transform.is_identity() &&
+           self.render_mode != FontRenderMode::Subpixel &&
+           !self.use_subpixel_position() {
+            max_size / FONT_SIZE_LIMIT
+        } else {
+            1.0
+        }
+    }
 }
 
 #[repr(u32)]
 #[derive(Copy, Clone, Hash, PartialEq, Eq, Debug, Ord, PartialOrd)]
 pub enum SubpixelDirection {
     None = 0,
     Horizontal,
     Vertical,
--- a/gfx/webrender/src/glyph_rasterizer/pathfinder.rs
+++ b/gfx/webrender/src/glyph_rasterizer/pathfinder.rs
@@ -111,38 +111,40 @@ impl GlyphRasterizer {
                                                                           &render_task_cache_key);
         Some((cache_item, font.get_glyph_format()))
     }
 
     pub(in super) fn request_glyph_from_pathfinder_if_necessary(
         &mut self,
         glyph_key: &GlyphKey,
         font: &FontInstance,
+        scale: f32,
         cached_glyph_info: CachedGlyphInfo,
         texture_cache: &mut TextureCache,
         gpu_cache: &mut GpuCache,
         render_task_cache: &mut RenderTaskCache,
         render_task_tree: &mut RenderTaskTree,
         render_passes: &mut SpecialRenderPasses)
     -> Result<(RenderTaskCacheEntryHandle,GlyphFormat), ()>
     {
         let mut pathfinder_font_context = self.font_contexts.lock_pathfinder_context();
         let render_task_cache_key = cached_glyph_info.render_task_cache_key;
         let (glyph_origin, glyph_size) = (cached_glyph_info.origin, render_task_cache_key.size);
-        let user_data = [glyph_origin.x as f32, (glyph_origin.y - glyph_size.height) as f32, 1.0];
+        let user_data = [glyph_origin.x as f32, (glyph_origin.y - glyph_size.height) as f32, scale];
         let handle = try!(render_task_cache.request_render_task(render_task_cache_key,
                                                                 texture_cache,
                                                                 gpu_cache,
                                                                 render_task_tree,
                                                                 Some(user_data),
                                                                 false,
                                                                 |render_tasks| {
             // TODO(pcwalton): Non-subpixel font render mode.
             request_render_task_from_pathfinder(glyph_key,
                                                 font,
+                                                scale,
                                                 &glyph_origin,
                                                 &glyph_size,
                                                 &mut *pathfinder_font_context,
                                                 font.render_mode,
                                                 render_tasks,
                                                 render_passes)
         }));
         Ok((handle, font.get_glyph_format()))
@@ -158,16 +160,19 @@ impl GlyphRasterizer {
         render_task_cache: &mut RenderTaskCache,
         render_task_tree: &mut RenderTaskTree,
         render_passes: &mut SpecialRenderPasses,
     ) {
         debug_assert!(self.font_contexts.lock_shared_context().has_font(&font.font_key));
 
         let glyph_key_cache = glyph_cache.get_glyph_key_cache_for_font_mut(font.clone());
 
+        let (x_scale, y_scale) = font.transform.compute_scale().unwrap_or((1.0, 1.0));
+        let scale = font.oversized_scale_factor(x_scale, y_scale) as f32;
+
         // select glyphs that have not been requested yet.
         for glyph_key in glyph_keys {
             let mut cached_glyph_info = None;
             match glyph_key_cache.entry(glyph_key.clone()) {
                 Entry::Occupied(mut entry) => {
                     let value = entry.into_mut();
                     match *value {
                         GlyphCacheEntry::Cached(ref glyph_info) => {
@@ -179,17 +184,17 @@ impl GlyphRasterizer {
                 Entry::Vacant(_) => {}
             }
 
             if cached_glyph_info.is_none() {
                 let mut pathfinder_font_context = self.font_contexts.lock_pathfinder_context();
 
                 let pathfinder_font_instance = pathfinder_font_renderer::FontInstance {
                     font_key: font.font_key.clone(),
-                    size: font.size,
+                    size: font.size.scale_by(scale.recip()),
                 };
 
                 // TODO: pathfinder will need to support 2D subpixel offset
                 let pathfinder_subpixel_offset =
                     pathfinder_font_renderer::SubpixelOffset(glyph_key.subpixel_offset.0 as u8);
                 let pathfinder_glyph_key =
                     pathfinder_font_renderer::GlyphKey::new(glyph_key.index,
                                                             pathfinder_subpixel_offset);
@@ -211,16 +216,17 @@ impl GlyphRasterizer {
                     self.next_gpu_glyph_cache_key.0 += 1;
                 }
             }
 
             let handle = match cached_glyph_info {
                 Some(glyph_info) => {
                     match self.request_glyph_from_pathfinder_if_necessary(glyph_key,
                                                                           &font,
+                                                                          scale,
                                                                           glyph_info.clone(),
                                                                           texture_cache,
                                                                           gpu_cache,
                                                                           render_task_cache,
                                                                           render_task_tree,
                                                                           render_passes) {
                         Ok(_) => GlyphCacheEntry::Cached(glyph_info),
                         Err(_) => GlyphCacheEntry::Blank,
@@ -254,26 +260,28 @@ impl FontContexts {
 
 fn compute_embolden_amount(ppem: f32) -> TypedVector2D<f32, DevicePixel> {
     TypedVector2D::new(f32::min(ppem * STEM_DARKENING_FACTOR_X, MAX_STEM_DARKENING_AMOUNT),
                        f32::min(ppem * STEM_DARKENING_FACTOR_Y, MAX_STEM_DARKENING_AMOUNT))
 }
 
 fn request_render_task_from_pathfinder(glyph_key: &GlyphKey,
                                        font: &FontInstance,
+                                       scale: f32,
                                        glyph_origin: &DeviceIntPoint,
                                        glyph_size: &DeviceIntSize,
                                        font_context: &mut PathfinderFontContext,
                                        render_mode: FontRenderMode,
                                        render_tasks: &mut RenderTaskTree,
                                        render_passes: &mut SpecialRenderPasses)
                                        -> Result<RenderTaskId, ()> {
+    let size = font.size.scale_by(scale.recip());
     let pathfinder_font_instance = pathfinder_font_renderer::FontInstance {
         font_key: font.font_key.clone(),
-        size: font.size,
+        size,
     };
 
     // TODO: pathfinder will need to support 2D subpixel offset
     let pathfinder_subpixel_offset =
         pathfinder_font_renderer::SubpixelOffset(glyph_key.subpixel_offset.0 as u8);
     let glyph_subpixel_offset: f64 = glyph_key.subpixel_offset.0.into();
     let pathfinder_glyph_key = pathfinder_font_renderer::GlyphKey::new(glyph_key.index,
                                                                        pathfinder_subpixel_offset);
@@ -286,17 +294,17 @@ fn request_render_task_from_pathfinder(g
     mesh.push_stencil_segments(CubicToQuadraticTransformer::new(outline.iter(), tolerance));
     mesh.push_stencil_normals(CubicToQuadraticTransformer::new(outline.iter(), tolerance));
 
     // FIXME(pcwalton): Support vertical subpixel offsets.
     // FIXME(pcwalton): Embolden amount should be 0 on macOS if "Use LCD font
     // smoothing" is unchecked in System Preferences.
 
     let subpixel_offset = TypedPoint2D::new(glyph_subpixel_offset as f32, 0.0);
-    let embolden_amount = compute_embolden_amount(font.size.to_f32_px());
+    let embolden_amount = compute_embolden_amount(size.to_f32_px());
 
     let location = RenderTaskLocation::Dynamic(None, Some(*glyph_size));
     let glyph_render_task = RenderTask::new_glyph(location,
                                                   mesh,
                                                   &glyph_origin,
                                                   &subpixel_offset,
                                                   font.render_mode,
                                                   &embolden_amount);
--- a/gfx/webrender/src/platform/macos/font.rs
+++ b/gfx/webrender/src/platform/macos/font.rs
@@ -485,17 +485,18 @@ impl FontContext {
                 };
             }
         }
     }
 
     #[cfg(not(feature = "pathfinder"))]
     pub fn rasterize_glyph(&mut self, font: &FontInstance, key: &GlyphKey) -> GlyphRasterResult {
         let (x_scale, y_scale) = font.transform.compute_scale().unwrap_or((1.0, 1.0));
-        let size = font.size.scale_by(y_scale as f32);
+        let scale = font.oversized_scale_factor(x_scale, y_scale);
+        let size = font.size.scale_by((y_scale / scale) as f32);
         let ct_font = match self.get_ct_font(font.font_key, size, &font.variations) {
             Some(font) => font,
             None => return GlyphRasterResult::LoadFailed,
         };
 
         let bitmap = is_bitmap_font(&ct_font);
         let (mut shape, (x_offset, y_offset)) = if bitmap {
             (FontTransform::identity(), (0.0, 0.0))
@@ -524,17 +525,17 @@ impl FontContext {
                 ty: 0.0,
             })
         } else {
             None
         };
 
         let glyph = key.index as CGGlyph;
         let (strike_scale, pixel_step) = if bitmap { (y_scale, 1.0) } else { (x_scale, y_scale / x_scale) };
-        let extra_strikes = font.get_extra_strikes(strike_scale);
+        let extra_strikes = font.get_extra_strikes(strike_scale / scale);
         let metrics = get_glyph_metrics(
             &ct_font,
             transform.as_ref(),
             glyph,
             x_offset,
             y_offset,
             extra_strikes as f64 * pixel_step,
         );
@@ -719,17 +720,17 @@ impl FontContext {
             }
         }
 
         GlyphRasterResult::Bitmap(RasterizedGlyph {
             left: metrics.rasterized_left as f32,
             top: metrics.rasterized_ascent as f32,
             width: metrics.rasterized_width,
             height: metrics.rasterized_height,
-            scale: if bitmap { y_scale.recip() as f32 } else { 1.0 },
+            scale: (if bitmap { scale / y_scale } else { scale }) as f32,
             format: if bitmap { GlyphFormat::ColorBitmap } else { font.get_glyph_format() },
             bytes: rasterized_pixels,
         })
     }
 }
 
 #[cfg(feature = "pathfinder")]
 impl<'a> Into<CGFont> for NativeFontHandleWrapper<'a> {
--- a/gfx/webrender/src/platform/unix/font.rs
+++ b/gfx/webrender/src/platform/unix/font.rs
@@ -231,17 +231,17 @@ impl FontContext {
 
     pub fn delete_font(&mut self, font_key: &FontKey) {
         if let Some(face) = self.faces.remove(font_key) {
             let result = unsafe { FT_Done_Face(face.face) };
             assert!(succeeded(result));
         }
     }
 
-    fn load_glyph(&self, font: &FontInstance, glyph: &GlyphKey) -> Option<FT_GlyphSlot> {
+    fn load_glyph(&self, font: &FontInstance, glyph: &GlyphKey) -> Option<(FT_GlyphSlot, f32)> {
         debug_assert!(self.faces.contains_key(&font.font_key));
         let face = self.faces.get(&font.font_key).unwrap();
 
         let mut load_flags = FT_LOAD_DEFAULT;
         let FontInstancePlatformOptions { mut hinting, .. } = font.platform_options.unwrap_or_default();
         // Disable hinting if there is a non-axis-aligned transform.
         if font.flags.contains(FontInstanceFlags::SYNTHETIC_ITALICS) ||
            ((font.transform.scale_x != 0.0 || font.transform.scale_y != 0.0) &&
@@ -278,23 +278,24 @@ impl FontContext {
         if font.flags.contains(FontInstanceFlags::VERTICAL_LAYOUT) {
             load_flags |= FT_LOAD_VERTICAL_LAYOUT;
         }
 
         load_flags |= FT_LOAD_COLOR;
         load_flags |= FT_LOAD_IGNORE_GLOBAL_ADVANCE_WIDTH;
 
         let (x_scale, y_scale) = font.transform.compute_scale().unwrap_or((1.0, 1.0));
+        let scale = font.oversized_scale_factor(x_scale, y_scale);
         let req_size = font.size.to_f64_px();
         let face_flags = unsafe { (*face.face).face_flags };
         let mut result = if (face_flags & (FT_FACE_FLAG_FIXED_SIZES as FT_Long)) != 0 &&
                             (face_flags & (FT_FACE_FLAG_SCALABLE as FT_Long)) == 0 &&
                             (load_flags & FT_LOAD_NO_BITMAP) == 0 {
             unsafe { FT_Set_Transform(face.face, ptr::null_mut(), ptr::null_mut()) };
-            self.choose_bitmap_size(face.face, req_size * y_scale)
+            self.choose_bitmap_size(face.face, req_size * y_scale / scale)
         } else {
             let mut shape = font.transform.invert_scale(x_scale, y_scale);
             if font.flags.contains(FontInstanceFlags::FLIP_X) {
                 shape = shape.flip_x();
             }
             if font.flags.contains(FontInstanceFlags::FLIP_Y) {
                 shape = shape.flip_y();
             }
@@ -309,18 +310,18 @@ impl FontContext {
                 xy: (shape.skew_x * -65536.0) as FT_Fixed,
                 yx: (shape.skew_y * -65536.0) as FT_Fixed,
                 yy: (shape.scale_y * 65536.0) as FT_Fixed,
             };
             unsafe {
                 FT_Set_Transform(face.face, &mut ft_shape, ptr::null_mut());
                 FT_Set_Char_Size(
                     face.face,
-                    (req_size * x_scale * 64.0 + 0.5) as FT_F26Dot6,
-                    (req_size * y_scale * 64.0 + 0.5) as FT_F26Dot6,
+                    (req_size * x_scale / scale * 64.0 + 0.5) as FT_F26Dot6,
+                    (req_size * y_scale / scale * 64.0 + 0.5) as FT_F26Dot6,
                     0,
                     0,
                 )
             }
         };
 
         if succeeded(result) {
             result = unsafe { FT_Load_Glyph(face.face, glyph.index as FT_UInt, load_flags as FT_Int32) };
@@ -331,18 +332,21 @@ impl FontContext {
             assert!(slot != ptr::null_mut());
 
             if font.flags.contains(FontInstanceFlags::SYNTHETIC_BOLD) {
                 unsafe { FT_GlyphSlot_Embolden(slot) };
             }
 
             let format = unsafe { (*slot).format };
             match format {
-                FT_Glyph_Format::FT_GLYPH_FORMAT_OUTLINE |
-                FT_Glyph_Format::FT_GLYPH_FORMAT_BITMAP => Some(slot),
+                FT_Glyph_Format::FT_GLYPH_FORMAT_BITMAP => {
+                    let y_size = unsafe { (*(*(*slot).face).size).metrics.y_ppem };
+                    Some((slot, req_size as f32 / y_size as f32))
+                }
+                FT_Glyph_Format::FT_GLYPH_FORMAT_OUTLINE => Some((slot, scale as f32)),
                 _ => {
                     error!("Unsupported format");
                     debug!("format={:?}", format);
                     None
                 }
             }
         } else {
             error!("Unable to load glyph");
@@ -409,77 +413,81 @@ impl FontContext {
         cbox
     }
 
     fn get_glyph_dimensions_impl(
         &self,
         slot: FT_GlyphSlot,
         font: &FontInstance,
         glyph: &GlyphKey,
-        transform_bitmaps: bool,
+        use_transform: Option<f32>,
     ) -> Option<GlyphDimensions> {
-        let metrics = unsafe { &(*slot).metrics };
-
-        let mut advance = metrics.horiAdvance as f32 / 64.0;
-        match unsafe { (*slot).format } {
+        let format = unsafe { (*slot).format };
+        let (mut left, mut top, mut width, mut height) = match format {
             FT_Glyph_Format::FT_GLYPH_FORMAT_BITMAP => {
-                let mut left = unsafe { (*slot).bitmap_left };
-                let mut top = unsafe { (*slot).bitmap_top };
-                let mut width = unsafe { (*slot).bitmap.width };
-                let mut height = unsafe { (*slot).bitmap.rows };
-                if transform_bitmaps {
-                    let y_size = unsafe { (*(*(*slot).face).size).metrics.y_ppem };
-                    let scale = font.size.to_f32_px() / y_size as f32;
-                    let x0 = left as f32 * scale;
-                    let x1 = width as f32 * scale + x0;
-                    let y1 = top as f32 * scale;
-                    let y0 = y1 - height as f32 * scale;
-                    left = x0.round() as i32;
-                    top = y1.round() as i32;
-                    width = (x1.ceil() - x0.floor()) as u32;
-                    height = (y1.ceil() - y0.floor()) as u32;
-                    advance *= scale;
-                    if font.flags.contains(FontInstanceFlags::SYNTHETIC_ITALICS) {
-                        let (skew_min, skew_max) = get_skew_bounds(top - height as i32, top);
-                        left += skew_min as i32;
-                        width += (skew_max - skew_min) as u32;
-                    }
-                    if font.flags.contains(FontInstanceFlags::TRANSPOSE) {
-                        mem::swap(&mut width, &mut height);
-                        mem::swap(&mut left, &mut top);
-                        left -= width as i32;
-                        top += height as i32;
-                    }
-                    if font.flags.contains(FontInstanceFlags::FLIP_X) {
-                        left = -(left + width as i32);
-                    }
-                    if font.flags.contains(FontInstanceFlags::FLIP_Y) {
-                        top = -(top - height as i32);
-                    }
-                }
-                Some(GlyphDimensions {
-                    left,
-                    top,
-                    width,
-                    height,
-                    advance,
-                })
+                unsafe { (
+                    (*slot).bitmap_left,
+                    (*slot).bitmap_top,
+                    (*slot).bitmap.width,
+                    (*slot).bitmap.rows,
+                ) }
             }
             FT_Glyph_Format::FT_GLYPH_FORMAT_OUTLINE => {
                 let cbox = self.get_bounding_box(slot, font, glyph);
-                Some(GlyphDimensions {
-                    left: (cbox.xMin >> 6) as i32,
-                    top: (cbox.yMax >> 6) as i32,
-                    width: ((cbox.xMax - cbox.xMin) >> 6) as u32,
-                    height: ((cbox.yMax - cbox.yMin) >> 6) as u32,
-                    advance,
-                })
+                (
+                    (cbox.xMin >> 6) as i32,
+                    (cbox.yMax >> 6) as i32,
+                    ((cbox.xMax - cbox.xMin) >> 6) as u32,
+                    ((cbox.yMax - cbox.yMin) >> 6) as u32,
+                )
+            }
+            _ => return None,
+        };
+        let mut advance = unsafe { (*slot).metrics.horiAdvance as f32 / 64.0 };
+        if let Some(scale) = use_transform {
+            if scale != 1.0 {
+                let x0 = left as f32 * scale;
+                let x1 = width as f32 * scale + x0;
+                let y1 = top as f32 * scale;
+                let y0 = y1 - height as f32 * scale;
+                left = x0.round() as i32;
+                top = y1.round() as i32;
+                width = (x1.ceil() - x0.floor()) as u32;
+                height = (y1.ceil() - y0.floor()) as u32;
+                advance *= scale;
             }
-            _ => None,
+            // An outline glyph's cbox would have already been transformed inside FT_Load_Glyph,
+            // so only handle bitmap glyphs which are not handled by FT_Load_Glyph.
+            if format == FT_Glyph_Format::FT_GLYPH_FORMAT_BITMAP {
+                if font.flags.contains(FontInstanceFlags::SYNTHETIC_ITALICS) {
+                    let (skew_min, skew_max) = get_skew_bounds(top - height as i32, top);
+                    left += skew_min as i32;
+                    width += (skew_max - skew_min) as u32;
+                }
+                if font.flags.contains(FontInstanceFlags::TRANSPOSE) {
+                    mem::swap(&mut width, &mut height);
+                    mem::swap(&mut left, &mut top);
+                    left -= width as i32;
+                    top += height as i32;
+                }
+                if font.flags.contains(FontInstanceFlags::FLIP_X) {
+                    left = -(left + width as i32);
+                }
+                if font.flags.contains(FontInstanceFlags::FLIP_Y) {
+                    top = -(top - height as i32);
+                }
+            }
         }
+        Some(GlyphDimensions {
+            left,
+            top,
+            width,
+            height,
+            advance,
+        })
     }
 
     pub fn get_glyph_index(&mut self, font_key: FontKey, ch: char) -> Option<u32> {
         let face = self.faces.get(&font_key).expect("Unknown font key!");
         unsafe {
             let idx = FT_Get_Char_Index(face.face, ch as _);
             if idx != 0 {
                 Some(idx)
@@ -490,17 +498,17 @@ impl FontContext {
     }
 
     pub fn get_glyph_dimensions(
         &mut self,
         font: &FontInstance,
         key: &GlyphKey,
     ) -> Option<GlyphDimensions> {
         let slot = self.load_glyph(font, key);
-        slot.and_then(|slot| self.get_glyph_dimensions_impl(slot, font, key, true))
+        slot.and_then(|(slot, scale)| self.get_glyph_dimensions_impl(slot, font, key, Some(scale)))
     }
 
     fn choose_bitmap_size(&self, face: FT_Face, requested_size: f64) -> FT_Error {
         let mut best_dist = unsafe { *(*face).available_sizes.offset(0) }.y_ppem as f64 / 64.0 - requested_size;
         let mut best_size = 0;
         let num_fixed_sizes = unsafe { (*face).num_fixed_sizes };
         for i in 1 .. num_fixed_sizes {
             // Distance is positive if strike is larger than desired size,
@@ -587,40 +595,38 @@ impl FontContext {
             false
         } else {
             true
         }
     }
 
     #[cfg(not(feature = "pathfinder"))]
     pub fn rasterize_glyph(&mut self, font: &FontInstance, key: &GlyphKey) -> GlyphRasterResult {
-        let slot = match self.load_glyph(font, key) {
-            Some(slot) => slot,
+        let (slot, scale) = match self.load_glyph(font, key) {
+            Some(val) => val,
             None => return GlyphRasterResult::LoadFailed,
         };
 
         // Get dimensions of the glyph, to see if we need to rasterize it.
-        let dimensions = match self.get_glyph_dimensions_impl(slot, font, key, false) {
+        // Don't apply scaling to the dimensions, as the glyph cache needs to know the actual
+        // footprint of the glyph.
+        let dimensions = match self.get_glyph_dimensions_impl(slot, font, key, None) {
             Some(val) => val,
             None => return GlyphRasterResult::LoadFailed,
         };
         let GlyphDimensions { mut left, mut top, width, height, .. } = dimensions;
 
         // For spaces and other non-printable characters, early out.
         if width == 0 || height == 0 {
             return GlyphRasterResult::LoadFailed;
         }
 
         let format = unsafe { (*slot).format };
-        let mut scale = 1.0;
         match format {
-            FT_Glyph_Format::FT_GLYPH_FORMAT_BITMAP => {
-                let y_size = unsafe { (*(*(*slot).face).size).metrics.y_ppem };
-                scale = font.size.to_f32_px() / y_size as f32;
-            }
+            FT_Glyph_Format::FT_GLYPH_FORMAT_BITMAP => {}
             FT_Glyph_Format::FT_GLYPH_FORMAT_OUTLINE => {
                 if !self.rasterize_glyph_outline(slot, font, key) {
                     return GlyphRasterResult::LoadFailed;
                 }
             }
             _ => {
                 error!("Unsupported format");
                 debug!("format={:?}", format);
--- a/gfx/webrender/src/platform/windows/font.rs
+++ b/gfx/webrender/src/platform/windows/font.rs
@@ -380,18 +380,19 @@ impl FontContext {
             FontRenderMode::Subpixel => {
                 font.color = font.color.quantize();
             }
         }
     }
 
     #[cfg(not(feature = "pathfinder"))]
     pub fn rasterize_glyph(&mut self, font: &FontInstance, key: &GlyphKey) -> GlyphRasterResult {
-        let (.., y_scale) = font.transform.compute_scale().unwrap_or((1.0, 1.0));
-        let size = (font.size.to_f64_px() * y_scale) as f32;
+        let (x_scale, y_scale) = font.transform.compute_scale().unwrap_or((1.0, 1.0));
+        let scale = font.oversized_scale_factor(x_scale, y_scale);
+        let size = (font.size.to_f64_px() * y_scale / scale) as f32;
         let bitmaps = is_bitmap_font(font);
         let (mut shape, (x_offset, y_offset)) = if bitmaps {
             (FontTransform::identity(), (0.0, 0.0))
         } else {
             (font.transform.invert_scale(y_scale, y_scale), font.get_subpx_offset(key))
         };
         if font.flags.contains(FontInstanceFlags::FLIP_X) {
             shape = shape.flip_x();
@@ -446,17 +447,17 @@ impl FontContext {
         };
         lut_correction.preblend(&mut bgra_pixels, font.color);
 
         GlyphRasterResult::Bitmap(RasterizedGlyph {
             left: bounds.left as f32,
             top: -bounds.top as f32,
             width,
             height,
-            scale: if bitmaps { y_scale.recip() as f32 } else { 1.0 },
+            scale: (if bitmaps { scale / y_scale } else { scale }) as f32,
             format: if bitmaps { GlyphFormat::Bitmap } else { font.get_glyph_format() },
             bytes: bgra_pixels,
         })
     }
 }
 
 #[cfg(feature = "pathfinder")]
 impl<'a> From<NativeFontHandleWrapper<'a>> for PathfinderComPtr<IDWriteFontFace> {
--- a/gfx/webrender/src/prim_store.rs
+++ b/gfx/webrender/src/prim_store.rs
@@ -12,17 +12,17 @@ use app_units::Au;
 use border::{BorderCacheKey, BorderRenderTaskInfo};
 use box_shadow::BLUR_SAMPLE_SCALE;
 use clip_scroll_tree::{ClipChainIndex, ClipScrollNodeIndex, CoordinateSystemId};
 use clip_scroll_node::ClipScrollNode;
 use clip::{ClipChain, ClipChainNode, ClipChainNodeIter, ClipChainNodeRef, ClipSource};
 use clip::{ClipSourcesHandle, ClipWorkItem};
 use frame_builder::{FrameBuildingContext, FrameBuildingState, PictureContext, PictureState};
 use frame_builder::PrimitiveRunContext;
-use glyph_rasterizer::{FontInstance, FontTransform, GlyphKey};
+use glyph_rasterizer::{FontInstance, FontTransform, GlyphKey, FONT_SIZE_LIMIT};
 use gpu_cache::{GpuBlockData, GpuCache, GpuCacheAddress, GpuCacheHandle, GpuDataRequest,
                 ToGpuBlocks};
 use gpu_types::{BrushFlags, ClipChainRectIndex};
 use image::{for_each_tile, for_each_repetition};
 use picture::{PictureCompositeMode, PictureId, PicturePrimitive};
 #[cfg(debug_assertions)]
 use render_backend::FrameId;
 use render_task::{BlitSource, RenderTask, RenderTaskCacheKey};
@@ -768,37 +768,40 @@ impl TextRunPrimitiveCpu {
             shadow,
             glyph_raster_space,
         }
     }
 
     pub fn get_font(
         &self,
         device_pixel_scale: DevicePixelScale,
-        transform: Option<LayoutToWorldTransform>,
+        transform: LayoutToWorldTransform,
     ) -> FontInstance {
         let mut font = self.font.clone();
         font.size = font.size.scale_by(device_pixel_scale.0);
-        if let Some(transform) = transform {
-            if transform.has_perspective_component() ||
-               !transform.has_2d_inverse() ||
-               self.glyph_raster_space != GlyphRasterSpace::Screen {
-                font.disable_subpixel_aa();
-                font.disable_subpixel_position();
-            } else {
-                font.transform = FontTransform::from(&transform).quantize();
-            }
+        // Only support transforms that can be coerced to simple 2D transforms.
+        if transform.has_perspective_component() ||
+           !transform.has_2d_inverse() ||
+           // Font sizes larger than the limit need to be scaled, thus can't use subpixels.
+           transform.exceeds_2d_scale(FONT_SIZE_LIMIT / font.size.to_f64_px()) ||
+           // Otherwise, ensure the font is rasterized in screen-space.
+           self.glyph_raster_space != GlyphRasterSpace::Screen {
+            font.disable_subpixel_aa();
+            font.disable_subpixel_position();
+        } else {
+            // Quantize the transform to minimize thrashing of the glyph cache.
+            font.transform = FontTransform::from(&transform).quantize();
         }
         font
     }
 
     fn prepare_for_render(
         &mut self,
         device_pixel_scale: DevicePixelScale,
-        transform: Option<LayoutToWorldTransform>,
+        transform: LayoutToWorldTransform,
         allow_subpixel_aa: bool,
         display_list: &BuiltDisplayList,
         frame_building_state: &mut FrameBuildingState,
     ) {
         if !allow_subpixel_aa && self.font.bg_color.a == 0 {
             self.font.disable_subpixel_aa();
         }
 
@@ -1485,17 +1488,17 @@ impl PrimitiveStore {
         {
             metadata.prepared_frame_id = frame_state.render_tasks.frame_id();
         }
 
         match metadata.prim_kind {
             PrimitiveKind::TextRun => {
                 let text = &mut self.cpu_text_runs[metadata.cpu_prim_index.0];
                 // The transform only makes sense for screen space rasterization
-                let transform = Some(prim_run_context.scroll_node.world_content_transform.into());
+                let transform = prim_run_context.scroll_node.world_content_transform.into();
                 text.prepare_for_render(
                     frame_context.device_pixel_scale,
                     transform,
                     pic_context.allow_subpixel_aa,
                     pic_context.display_list,
                     frame_state,
                 );
             }
--- a/gfx/webrender/src/render_backend.rs
+++ b/gfx/webrender/src/render_backend.rs
@@ -714,19 +714,24 @@ impl RenderBackend {
                     SceneBuilderResult::Transaction {
                         document_id,
                         mut built_scene,
                         resource_updates,
                         frame_ops,
                         render,
                         result_tx,
                     } => {
+                        let mut ops = DocumentOps::nop();
                         if let Some(doc) = self.documents.get_mut(&document_id) {
                             if let Some(mut built_scene) = built_scene.take() {
                                 doc.new_async_scene_ready(built_scene);
+                                // After applying the new scene we need to
+                                // rebuild the hit-tester, so we trigger a render
+                                // step.
+                                ops = DocumentOps::render();
                             }
                             if let Some(tx) = result_tx {
                                 let (resume_tx, resume_rx) = channel();
                                 tx.send(SceneSwapResult::Complete(resume_tx)).unwrap();
                                 // Block until the post-swap hook has completed on
                                 // the scene builder thread. We need to do this before
                                 // we can sample from the sampler hook which might happen
                                 // in the update_document call below.
@@ -745,23 +750,23 @@ impl RenderBackend {
                         let transaction_msg = TransactionMsg {
                             scene_ops: Vec::new(),
                             frame_ops,
                             resource_updates,
                             generate_frame: render,
                             use_scene_builder_thread: false,
                         };
 
-                        if !transaction_msg.is_empty() {
+                        if !transaction_msg.is_empty() || ops.render {
                             self.update_document(
                                 document_id,
                                 transaction_msg,
                                 &mut frame_counter,
                                 &mut profile_counters,
-                                DocumentOps::render(),
+                                ops,
                             );
                         }
                     },
                     SceneBuilderResult::FlushComplete(tx) => {
                         tx.send(()).ok();
                     }
                     SceneBuilderResult::Stopped => {
                         panic!("We haven't sent a Stop yet, how did we get a Stopped back?");
--- a/gfx/webrender/src/renderer.rs
+++ b/gfx/webrender/src/renderer.rs
@@ -11,17 +11,17 @@
 
 use api::{BlobImageRenderer, ColorF, DeviceIntPoint, DeviceIntRect, DeviceIntSize};
 use api::{DeviceUintPoint, DeviceUintRect, DeviceUintSize, DocumentId, Epoch, ExternalImageId};
 use api::{ExternalImageType, FontRenderMode, FrameMsg, ImageFormat, PipelineId};
 use api::{RenderApiSender, RenderNotifier, TexelRect, TextureTarget};
 use api::{channel};
 use api::DebugCommand;
 use api::channel::PayloadReceiverHelperMethods;
-use batch::{BatchKind, BatchTextures, BrushBatchKind, TransformBatchKind};
+use batch::{BatchKind, BatchTextures, BrushBatchKind};
 #[cfg(any(feature = "capture", feature = "replay"))]
 use capture::{CaptureConfig, ExternalCaptureImage, PlainExternalImage};
 use debug_colors;
 use device::{DepthFunction, Device, FrameId, Program, UploadMethod, Texture, PBO};
 use device::{ExternalTexture, FBOId, TextureSlot};
 use device::{FileWatcherHandler, ShaderError, TextureFilter,
              VertexUsageHint, VAO, VBO, CustomVAO};
 use device::{ProgramCache, ReadPixelsFormat};
@@ -109,17 +109,17 @@ const GPU_TAG_BRUSH_YUV_IMAGE: GpuProfil
     color: debug_colors::DARKGREEN,
 };
 const GPU_TAG_BRUSH_MIXBLEND: GpuProfileTag = GpuProfileTag {
     label: "B_MixBlend",
     color: debug_colors::MAGENTA,
 };
 const GPU_TAG_BRUSH_BLEND: GpuProfileTag = GpuProfileTag {
     label: "B_Blend",
-    color: debug_colors::LIGHTBLUE,
+    color: debug_colors::ORANGE,
 };
 const GPU_TAG_BRUSH_IMAGE: GpuProfileTag = GpuProfileTag {
     label: "B_Image",
     color: debug_colors::SPRINGGREEN,
 };
 const GPU_TAG_BRUSH_SOLID: GpuProfileTag = GpuProfileTag {
     label: "B_Solid",
     color: debug_colors::RED,
@@ -165,48 +165,33 @@ const GPU_SAMPLER_TAG_OPAQUE: GpuProfile
     label: "Opaque Pass",
     color: debug_colors::BLACK,
 };
 const GPU_SAMPLER_TAG_TRANSPARENT: GpuProfileTag = GpuProfileTag {
     label: "Transparent Pass",
     color: debug_colors::BLACK,
 };
 
-impl TransformBatchKind {
-    #[cfg(feature = "debugger")]
-    fn debug_name(&self) -> &'static str {
-        match *self {
-            TransformBatchKind::TextRun(..) => "TextRun",
-        }
-    }
-
-    fn sampler_tag(&self) -> GpuProfileTag {
-        match *self {
-            TransformBatchKind::TextRun(..) => GPU_TAG_PRIM_TEXT_RUN,
-        }
-    }
-}
-
 impl BatchKind {
     #[cfg(feature = "debugger")]
     fn debug_name(&self) -> &'static str {
         match *self {
             BatchKind::SplitComposite => "SplitComposite",
             BatchKind::Brush(kind) => {
                 match kind {
                     BrushBatchKind::Solid => "Brush (Solid)",
                     BrushBatchKind::Image(..) => "Brush (Image)",
                     BrushBatchKind::Blend => "Brush (Blend)",
                     BrushBatchKind::MixBlend { .. } => "Brush (Composite)",
                     BrushBatchKind::YuvImage(..) => "Brush (YuvImage)",
                     BrushBatchKind::RadialGradient => "Brush (RadialGradient)",
                     BrushBatchKind::LinearGradient => "Brush (LinearGradient)",
                 }
             }
-            BatchKind::Transformable(_, batch_kind) => batch_kind.debug_name(),
+            BatchKind::TextRun(_) => "TextRun",
         }
     }
 
     fn sampler_tag(&self) -> GpuProfileTag {
         match *self {
             BatchKind::SplitComposite => GPU_TAG_PRIM_SPLIT_COMPOSITE,
             BatchKind::Brush(kind) => {
                 match kind {
@@ -214,17 +199,17 @@ impl BatchKind {
                     BrushBatchKind::Image(..) => GPU_TAG_BRUSH_IMAGE,
                     BrushBatchKind::Blend => GPU_TAG_BRUSH_BLEND,
                     BrushBatchKind::MixBlend { .. } => GPU_TAG_BRUSH_MIXBLEND,
                     BrushBatchKind::YuvImage(..) => GPU_TAG_BRUSH_YUV_IMAGE,
                     BrushBatchKind::RadialGradient => GPU_TAG_BRUSH_RADIAL_GRADIENT,
                     BrushBatchKind::LinearGradient => GPU_TAG_BRUSH_LINEAR_GRADIENT,
                 }
             }
-            BatchKind::Transformable(_, batch_kind) => batch_kind.sampler_tag(),
+            BatchKind::TextRun(_) => GPU_TAG_PRIM_TEXT_RUN,
         }
     }
 }
 
 bitflags! {
     #[derive(Default)]
     pub struct DebugFlags: u32 {
         const PROFILER_DBG      = 1 << 0;
--- a/gfx/webrender/src/shade.rs
+++ b/gfx/webrender/src/shade.rs
@@ -1,27 +1,26 @@
 /* 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::{
     YUV_COLOR_SPACES, YUV_FORMATS,
     YuvColorSpace, YuvFormat,
 };
-use batch::{BatchKey, BatchKind, BrushBatchKind, TransformBatchKind};
+use batch::{BatchKey, BatchKind, BrushBatchKind};
 use device::{Device, Program, ShaderError};
 use euclid::{Transform3D};
 use glyph_rasterizer::GlyphFormat;
 use renderer::{
     desc,
     MAX_VERTEX_TEXTURE_WIDTH,
     BlendMode, ImageBufferKind, RendererError, RendererOptions,
     TextureSampler, VertexArrayKind,
 };
-use util::TransformedRectKind;
 
 use gleam::gl::GlType;
 use time::precise_time_ns;
 
 
 impl ImageBufferKind {
     pub(crate) fn get_feature_string(&self) -> &'static str {
         match *self {
@@ -257,17 +256,16 @@ impl BrushShader {
         if let Some(dual_source) = self.dual_source {
             dual_source.deinit(device);
         }
     }
 }
 
 pub struct TextShader {
     simple: LazilyCompiledShader,
-    transform: LazilyCompiledShader,
     glyph_transform: LazilyCompiledShader,
 }
 
 impl TextShader {
     fn new(
         name: &'static str,
         device: &mut Device,
         features: &[&'static str],
@@ -276,62 +274,46 @@ impl TextShader {
         let simple = LazilyCompiledShader::new(
             ShaderKind::Text,
             name,
             features,
             device,
             precache,
         )?;
 
-        let mut transform_features = features.to_vec();
-        transform_features.push("TRANSFORM");
-
-        let transform = LazilyCompiledShader::new(
-            ShaderKind::Text,
-            name,
-            &transform_features,
-            device,
-            precache,
-        )?;
-
         let mut glyph_transform_features = features.to_vec();
         glyph_transform_features.push("GLYPH_TRANSFORM");
 
         let glyph_transform = LazilyCompiledShader::new(
             ShaderKind::Text,
             name,
             &glyph_transform_features,
             device,
             precache,
         )?;
 
-        Ok(TextShader { simple, transform, glyph_transform })
+        Ok(TextShader { simple, glyph_transform })
     }
 
     pub fn get(
         &mut self,
         glyph_format: GlyphFormat,
-        transform_kind: TransformedRectKind,
     ) -> &mut LazilyCompiledShader {
         match glyph_format {
             GlyphFormat::Alpha |
             GlyphFormat::Subpixel |
             GlyphFormat::Bitmap |
-            GlyphFormat::ColorBitmap => match transform_kind {
-                TransformedRectKind::AxisAligned => &mut self.simple,
-                TransformedRectKind::Complex => &mut self.transform,
-            }
+            GlyphFormat::ColorBitmap => &mut self.simple,
             GlyphFormat::TransformedAlpha |
             GlyphFormat::TransformedSubpixel => &mut self.glyph_transform,
         }
     }
 
     fn deinit(self, device: &mut Device) {
         self.simple.deinit(device);
-        self.transform.deinit(device);
         self.glyph_transform.deinit(device);
     }
 }
 
 fn create_prim_shader(
     name: &'static str,
     device: &mut Device,
     features: &[&'static str],
@@ -721,30 +703,22 @@ impl Shaders {
                             Self::get_yuv_shader_index(image_buffer_kind, format, color_space);
                         self.brush_yuv_image[shader_index]
                             .as_mut()
                             .expect("Unsupported YUV shader kind")
                     }
                 };
                 brush_shader.get(key.blend_mode)
             }
-            BatchKind::Transformable(transform_kind, batch_kind) => {
-                match batch_kind {
-                    TransformBatchKind::TextRun(glyph_format) => {
-                        let text_shader = match key.blend_mode {
-                            BlendMode::SubpixelDualSource => {
-                                &mut self.ps_text_run_dual_source
-                            }
-                            _ => {
-                                &mut self.ps_text_run
-                            }
-                        };
-                        return text_shader.get(glyph_format, transform_kind);
-                    }
-                }
+            BatchKind::TextRun(glyph_format) => {
+                let text_shader = match key.blend_mode {
+                    BlendMode::SubpixelDualSource => &mut self.ps_text_run_dual_source,
+                    _ => &mut self.ps_text_run,
+                };
+                text_shader.get(glyph_format)
             }
         }
     }
 
     pub fn deinit(self, device: &mut Device) {
         self.cs_blur_a8.deinit(device);
         self.cs_blur_rgba8.deinit(device);
         self.brush_solid.deinit(device);
--- a/gfx/webrender/src/util.rs
+++ b/gfx/webrender/src/util.rs
@@ -15,16 +15,17 @@ use std::{i32, f32};
 // Matches the definition of SK_ScalarNearlyZero in Skia.
 const NEARLY_ZERO: f32 = 1.0 / 4096.0;
 
 // TODO: Implement these in euclid!
 pub trait MatrixHelpers<Src, Dst> {
     fn preserves_2d_axis_alignment(&self) -> bool;
     fn has_perspective_component(&self) -> bool;
     fn has_2d_inverse(&self) -> bool;
+    fn exceeds_2d_scale(&self, limit: f64) -> bool;
     fn inverse_project(&self, target: &TypedPoint2D<f32, Dst>) -> Option<TypedPoint2D<f32, Src>>;
     fn inverse_rect_footprint(&self, rect: &TypedRect<f32, Dst>) -> TypedRect<f32, Src>;
     fn transform_kind(&self) -> TransformedRectKind;
     fn is_simple_translation(&self) -> bool;
     fn is_simple_2d_translation(&self) -> bool;
 }
 
 impl<Src, Dst> MatrixHelpers<Src, Dst> for TypedTransform3D<f32, Src, Dst> {
@@ -63,16 +64,24 @@ impl<Src, Dst> MatrixHelpers<Src, Dst> f
     fn has_perspective_component(&self) -> bool {
          self.m14 != 0.0 || self.m24 != 0.0 || self.m34 != 0.0 || self.m44 != 1.0
     }
 
     fn has_2d_inverse(&self) -> bool {
         self.m11 * self.m22 - self.m12 * self.m21 != 0.0
     }
 
+    // Check if the matrix post-scaling on either the X or Y axes could cause geometry
+    // transformed by this matrix to have scaling exceeding the supplied limit.
+    fn exceeds_2d_scale(&self, limit: f64) -> bool {
+        let limit2 = (limit * limit) as f32;
+        self.m11 * self.m11 + self.m12 * self.m12 > limit2 ||
+        self.m21 * self.m21 + self.m22 * self.m22 > limit2
+    }
+
     fn inverse_project(&self, target: &TypedPoint2D<f32, Dst>) -> Option<TypedPoint2D<f32, Src>> {
         let m: TypedTransform2D<f32, Src, Dst>;
         m = TypedTransform2D::column_major(
             self.m11 - target.x * self.m14,
             self.m21 - target.x * self.m24,
             self.m41 - target.x * self.m44,
             self.m12 - target.y * self.m14,
             self.m22 - target.y * self.m24,
--- a/gfx/webrender_bindings/revision.txt
+++ b/gfx/webrender_bindings/revision.txt
@@ -1,1 +1,1 @@
-aff9f409f3d6a3518c38c1f7755657f564c1083a
+dd30fbb21c876b252b805b607bd04f3bab1fd228
--- a/gfx/wrench/src/args.yaml
+++ b/gfx/wrench/src/args.yaml
@@ -77,51 +77,52 @@ subcommands:
               long: surface
               help: 'What rendered surface to save as PNG, one of: screen, gpu-cache'
               takes_value: true
           - INPUT:
               help: The input YAML file
               required: true
               index: 1
     - show:
-        about: show frame(s) described by YAML
+        about: show frame(s) described by YAML, binary recording, or capture
+        aliases: ['load', 'replay']
         args:
           - queue:
               short: q
               long: queue
-              help: How many frames to submit to WR ahead of time (default 1)
+              help: How many frames to submit to WR ahead of time (default 1) (YAML only)
               takes_value: true
           - include:
               long: include
-              help: Include the given element type. Can be specified multiple times. (rect/image/text/glyphs/border)
+              help: Include the given element type. Can be specified multiple times. (rect/image/text/glyphs/border) (YAML only)
               multiple: true
               takes_value: true
           - list-resources:
               long: list-resources
-              help: List the resources used by this YAML file
+              help: List the resources used by this render (YAML only)
           - watch:
               short: w
               long: watch
-              help: Watch the given YAML file, reloading whenever it changes
+              help: Watch the given file, reloading whenever it changes (YAML only)
+          - api:
+              long: api
+              help: Reissue Api messages for each frame (binary recording only)
+          - skip-uploads:
+              long: skip-uploads
+              help: Skip re-uploads while reissuing Api messages (BROKEN)
+          - play:
+              long: play
+              help: Play entire recording through, then quit (useful with --save) (binary recording only)
           - INPUT:
-              help: The input YAML file
+              help: The input YAML, binary recording, or capture directory
               required: true
               index: 1
     - replay:
         about: replay binary recording
         args:
-          - api:
-              long: api
-              help: Reissue Api messages for each frame
-          - skip-uploads:
-              long: skip-uploads
-              help: Skip re-uploads while reissuing Api messages (BROKEN)
-          - play:
-              long: play
-              help: Play entire recording through, then quit (useful with --save)
           - INPUT:
               help: The input binary file or directory
               required: true
               index: 1
     - reftest:
         about: run reftests
         args:
           - fuzz_tolerance:
@@ -148,16 +149,8 @@ subcommands:
           - first_filename:
               help: first benchmark file to compare
               required: true
               index: 1
           - second_filename:
               help: second benchmark file to compare
               required: true
               index: 2
-    - load:
-        about: load a capture
-        args:
-          - path:
-              help: directory containing the capture
-              takes_value: true
-              required: true
-              index: 1
--- a/gfx/wrench/src/main.rs
+++ b/gfx/wrench/src/main.rs
@@ -358,16 +358,41 @@ impl RenderNotifier for Notifier {
     }
 }
 
 fn create_notifier() -> (Box<RenderNotifier>, Receiver<NotifierEvent>) {
     let (tx, rx) = channel();
     (Box::new(Notifier { tx: tx }), rx)
 }
 
+fn rawtest(mut wrench: Wrench, window: &mut WindowWrapper, rx: Receiver<NotifierEvent>) {
+    RawtestHarness::new(&mut wrench, window, &rx).run();
+    wrench.shut_down(rx);
+}
+
+fn reftest<'a>(
+    mut wrench: Wrench,
+    window: &mut WindowWrapper,
+    subargs: &clap::ArgMatches<'a>,
+    rx: Receiver<NotifierEvent>
+) -> usize {
+    let dim = window.get_inner_size();
+    let base_manifest = Path::new("reftests/reftest.list");
+    let specific_reftest = subargs.value_of("REFTEST").map(|x| Path::new(x));
+    let mut reftest_options = ReftestOptions::default();
+    if let Some(allow_max_diff) = subargs.value_of("fuzz_tolerance") {
+        reftest_options.allow_max_difference = allow_max_diff.parse().unwrap_or(1);
+        reftest_options.allow_num_differences = dim.width as usize * dim.height as usize;
+    }
+    let num_failures = ReftestHarness::new(&mut wrench, window, &rx)
+        .run(base_manifest, specific_reftest, &reftest_options);
+    wrench.shut_down(rx);
+    num_failures
+}
+
 fn main() {
     #[cfg(feature = "env_logger")]
     env_logger::init();
 
     let args_yaml = load_yaml!("args.yaml");
     let args = clap::App::from_yaml(args_yaml)
         .setting(clap::AppSettings::ArgRequiredElseHelp)
         .get_matches();
@@ -435,87 +460,92 @@ fn main() {
         args.is_present("no_scissor"),
         args.is_present("no_batch"),
         args.is_present("precache"),
         args.is_present("slow_subpixel"),
         zoom_factor.unwrap_or(1.0),
         notifier,
     );
 
-    let mut thing = if let Some(subargs) = args.subcommand_matches("show") {
-        Box::new(YamlFrameReader::new_from_args(subargs)) as Box<WrenchThing>
-    } else if let Some(subargs) = args.subcommand_matches("replay") {
-        Box::new(BinaryFrameReader::new_from_args(subargs)) as Box<WrenchThing>
+    if let Some(subargs) = args.subcommand_matches("show") {
+        render(&mut wrench, &mut window, size, &mut events_loop, subargs);
     } else if let Some(subargs) = args.subcommand_matches("png") {
         let surface = match subargs.value_of("surface") {
             Some("screen") | None => png::ReadSurface::Screen,
             Some("gpu-cache") => png::ReadSurface::GpuCache,
             _ => panic!("Unknown surface argument value")
         };
         let reader = YamlFrameReader::new_from_args(subargs);
         png::png(&mut wrench, surface, &mut window, reader, rx.unwrap());
-        wrench.renderer.deinit();
-        return;
     } else if let Some(subargs) = args.subcommand_matches("reftest") {
-        let dim = window.get_inner_size();
-        let base_manifest = Path::new("reftests/reftest.list");
-        let specific_reftest = subargs.value_of("REFTEST").map(|x| Path::new(x));
-        let mut reftest_options = ReftestOptions::default();
-        if let Some(allow_max_diff) = subargs.value_of("fuzz_tolerance") {
-            reftest_options.allow_max_difference = allow_max_diff.parse().unwrap_or(1);
-            reftest_options.allow_num_differences = dim.width as usize * dim.height as usize;
-        }
-        let rx = rx.unwrap();
-        let num_failures = ReftestHarness::new(&mut wrench, &mut window, &rx)
-            .run(base_manifest, specific_reftest, &reftest_options);
-        wrench.shut_down(rx);
-        // exit with an error code to fail on CI
-        process::exit(num_failures as _);
+        // Exit with an error code in order to ensure the CI job fails.
+        process::exit(reftest(wrench, &mut window, subargs, rx.unwrap()) as _);
     } else if let Some(_) = args.subcommand_matches("rawtest") {
-        let rx = rx.unwrap();
-        {
-            let harness = RawtestHarness::new(&mut wrench, &mut window, &rx);
-            harness.run();
-        }
-        wrench.shut_down(rx);
+        rawtest(wrench, &mut window, rx.unwrap());
         return;
     } else if let Some(subargs) = args.subcommand_matches("perf") {
         // Perf mode wants to benchmark the total cost of drawing
         // a new displaty list each frame.
         wrench.rebuild_display_lists = true;
         let harness = PerfHarness::new(&mut wrench, &mut window, rx.unwrap());
         let base_manifest = Path::new("benchmarks/benchmarks.list");
         let filename = subargs.value_of("filename").unwrap();
         harness.run(base_manifest, filename);
         return;
     } else if let Some(subargs) = args.subcommand_matches("compare_perf") {
         let first_filename = subargs.value_of("first_filename").unwrap();
         let second_filename = subargs.value_of("second_filename").unwrap();
         perf::compare(first_filename, second_filename);
         return;
-    } else if let Some(subargs) = args.subcommand_matches("load") {
-        let path = PathBuf::from(subargs.value_of("path").unwrap());
-        let mut documents = wrench.api.load_capture(path);
+    } else {
+        panic!("Should never have gotten here! {:?}", args);
+    };
+
+    wrench.renderer.deinit();
+}
+
+fn render<'a>(
+    wrench: &mut Wrench,
+    window: &mut WindowWrapper,
+    size: DeviceUintSize,
+    events_loop: &mut Option<winit::EventsLoop>,
+    subargs: &clap::ArgMatches<'a>,
+) {
+    let input_path = subargs.value_of("INPUT").map(PathBuf::from).unwrap();
+
+    // If the input is a directory, we are looking at a capture.
+    let mut thing = if input_path.as_path().is_dir() {
+        let mut documents = wrench.api.load_capture(input_path);
         println!("loaded {:?}", documents.iter().map(|cd| cd.document_id).collect::<Vec<_>>());
         let captured = documents.swap_remove(0);
         window.resize(captured.window_size);
         wrench.document_id = captured.document_id;
         Box::new(captured) as Box<WrenchThing>
     } else {
-        panic!("Should never have gotten here! {:?}", args);
+        let extension = input_path
+            .extension()
+            .expect("Tried to render with an unknown file type.")
+            .to_str()
+            .expect("Tried to render with an unknown file type.");
+
+        match extension {
+            "yaml" => Box::new(YamlFrameReader::new_from_args(subargs)) as Box<WrenchThing>,
+            "bin" => Box::new(BinaryFrameReader::new_from_args(subargs)) as Box<WrenchThing>,
+            _ => panic!("Tried to render with an unknown file type."),
+        }
     };
 
     let mut show_help = false;
     let mut do_loop = false;
     let mut cpu_profile_index = 0;
     let mut cursor_position = WorldPoint::zero();
 
     let dim = window.get_inner_size();
     wrench.update(dim);
-    thing.do_frame(&mut wrench);
+    thing.do_frame(wrench);
 
     let mut body = |wrench: &mut Wrench, global_event: winit::Event| {
         if let Some(window_title) = wrench.take_title() {
             if !cfg!(windows) { //TODO: calling `set_title` from inside the `run_forever` loop is illegal...
                 window.set_title(&window_title);
             }
         }
 
@@ -660,22 +690,18 @@ fn main() {
             if do_loop {
                 thing.next_frame();
             }
         }
 
         winit::ControlFlow::Continue
     };
 
-    match events_loop {
+    match *events_loop {
         None => {
-            while body(&mut wrench, winit::Event::Awakened) == winit::ControlFlow::Continue {}
+            while body(wrench, winit::Event::Awakened) == winit::ControlFlow::Continue {}
             let rect = DeviceUintRect::new(DeviceUintPoint::zero(), size);
             let pixels = wrench.renderer.read_pixels_rgba8(rect);
             save_flipped("screenshot.png", pixels, size);
         }
-        Some(ref mut events_loop) => {
-            events_loop.run_forever(|event| body(&mut wrench, event));
-        }
+        Some(ref mut events_loop) => events_loop.run_forever(|event| body(wrench, event)),
     }
-
-    wrench.renderer.deinit();
 }