Bug 1467096 - Update webrender to commit dd30fbb21c876b252b805b607bd04f3bab1fd228. r=Gankro
authorKartikaya Gupta <kgupta@mozilla.com>
Fri, 15 Jun 2018 09:46:46 -0400
changeset 422695 7fbf8a9126e54addb86d01ff6ec1e62e7fda18e5
parent 422694 c1d664fefdff80e066dde0140c22e8ff74fe8694
child 422696 266c6cfb96a76daaa7bd2e3574f7d1d8ae60c4a6
push id104335
push useraiakab@mozilla.com
push dateFri, 15 Jun 2018 21:54:52 +0000
treeherdermozilla-inbound@76a5fb275ed8 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersGankro
bugs1467096
milestone62.0a1
first release with
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
last release without
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
Bug 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();
 }