Bug 1374730 - Update webrender to cset 519e51986308fc11d6ba6771f1c11ea6a3133921. r=jrmuizel
authorKartikaya Gupta <kgupta@mozilla.com>
Mon, 10 Jul 2017 07:19:51 -0400
changeset 368115 9d94771ce24a317bb3c97229531874fae293505f
parent 368114 3499845a85f716cb0810d175499935570b97948c
child 368116 f3f563d9b0064f5ddc2d9993eebc72262a0ac898
push id92410
push userkwierso@gmail.com
push dateTue, 11 Jul 2017 01:49:06 +0000
treeherdermozilla-inbound@a09f9b7135ad [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersjrmuizel
bugs1374730
milestone56.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 1374730 - Update webrender to cset 519e51986308fc11d6ba6771f1c11ea6a3133921. r=jrmuizel MozReview-Commit-ID: 81YYW87APLn
gfx/doc/README.webrender
gfx/webrender/Cargo.toml
gfx/webrender/benches/coalesce.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/nested_display_list.rs
gfx/webrender/examples/scrolling.rs
gfx/webrender/examples/yuv.rs
gfx/webrender/res/cs_clip_border.vs.glsl
gfx/webrender/res/cs_clip_image.vs.glsl
gfx/webrender/res/cs_clip_rectangle.vs.glsl
gfx/webrender/res/cs_text_run.vs.glsl
gfx/webrender/res/prim_shared.glsl
gfx/webrender/src/border.rs
gfx/webrender/src/clip_scroll_node.rs
gfx/webrender/src/clip_scroll_tree.rs
gfx/webrender/src/debug_colors.rs
gfx/webrender/src/debug_render.rs
gfx/webrender/src/device.rs
gfx/webrender/src/ellipse.rs
gfx/webrender/src/frame.rs
gfx/webrender/src/frame_builder.rs
gfx/webrender/src/glyph_rasterizer.rs
gfx/webrender/src/gpu_cache.rs
gfx/webrender/src/gpu_store.rs
gfx/webrender/src/internal_types.rs
gfx/webrender/src/lib.rs
gfx/webrender/src/mask_cache.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/profiler.rs
gfx/webrender/src/record.rs
gfx/webrender/src/render_backend.rs
gfx/webrender/src/render_task.rs
gfx/webrender/src/renderer.rs
gfx/webrender/src/resource_cache.rs
gfx/webrender/src/scene.rs
gfx/webrender/src/spring.rs
gfx/webrender/src/texture_cache.rs
gfx/webrender/src/tiling.rs
gfx/webrender/src/util.rs
gfx/webrender/src/webgl_stubs.rs
gfx/webrender/src/webgl_types.rs
gfx/webrender_api/Cargo.toml
gfx/webrender_api/src/api.rs
gfx/webrender_api/src/channel.rs
gfx/webrender_api/src/channel_ipc.rs
gfx/webrender_api/src/channel_mpsc.rs
gfx/webrender_api/src/color.rs
gfx/webrender_api/src/display_item.rs
gfx/webrender_api/src/display_list.rs
gfx/webrender_api/src/font.rs
gfx/webrender_api/src/image.rs
gfx/webrender_api/src/lib.rs
gfx/webrender_api/src/units.rs
gfx/webrender_api/src/webgl.rs
gfx/webrender_traits/Cargo.toml
gfx/webrender_traits/src/api.rs
gfx/webrender_traits/src/channel.rs
gfx/webrender_traits/src/channel_ipc.rs
gfx/webrender_traits/src/channel_mpsc.rs
gfx/webrender_traits/src/color.rs
gfx/webrender_traits/src/display_item.rs
gfx/webrender_traits/src/display_list.rs
gfx/webrender_traits/src/font.rs
gfx/webrender_traits/src/image.rs
gfx/webrender_traits/src/lib.rs
gfx/webrender_traits/src/units.rs
gfx/webrender_traits/src/webgl.rs
--- a/gfx/doc/README.webrender
+++ b/gfx/doc/README.webrender
@@ -74,9 +74,9 @@ there is another crate in m-c called moz
 the same folder to store its rust dependencies. If one of the libraries that is
 required by both mozjs_sys and webrender is updated without updating the other
 project's Cargo.lock file, that results in build bustage.
 This means that any time you do this sort of manual update of packages, you need
 to make sure that mozjs_sys also has its Cargo.lock file updated if needed, hence
 the need to run the cargo update command in js/src as well. Hopefully this will
 be resolved soon.
 
-Latest Commit: 1d6348023a4a4fdd89dce038640c5da906005acc
+Latest Commit: 519e51986308fc11d6ba6771f1c11ea6a3133921
--- a/gfx/webrender/Cargo.toml
+++ b/gfx/webrender/Cargo.toml
@@ -1,38 +1,38 @@
 [package]
 name = "webrender"
-version = "0.43.0"
+version = "0.47.0"
 authors = ["Glenn Watson <gw@intuitionlibrary.com>"]
 license = "MPL-2.0"
 repository = "https://github.com/servo/webrender"
 build = "build.rs"
 
 [features]
 default = ["freetype-lib", "webgl"]
 freetype-lib = ["freetype/servo-freetype-sys"]
 profiler = ["thread_profiler/thread_profiler"]
-webgl = ["offscreen_gl_context", "webrender_traits/webgl"]
+webgl = ["offscreen_gl_context", "webrender_api/webgl"]
 
 [dependencies]
 app_units = "0.5"
 bincode = "0.8"
 bit-set = "0.4"
 byteorder = "1.0"
-euclid = "0.15"
+euclid = "0.15.1"
 fnv = "1.0"
-gleam = "0.4.3"
+gleam = "0.4.7"
 lazy_static = "0.2"
 log = "0.3"
 num-traits = "0.1.32"
 offscreen_gl_context = {version = "0.11", features = ["serde", "osmesa"], optional = true}
 time = "0.1"
 rayon = "0.8"
-webrender_traits = {path = "../webrender_traits"}
-bitflags = "0.7"
+webrender_api = {path = "../webrender_api"}
+bitflags = "0.9"
 gamma-lut = "0.2"
 thread_profiler = "0.1.1"
 plane-split = "0.6"
 
 [dev-dependencies]
 angle = {git = "https://github.com/servo/angle", branch = "servo"}
 rand = "0.3"                # for the benchmarks
 servo-glutin = "0.11"     # for the example apps
@@ -40,9 +40,9 @@ servo-glutin = "0.11"     # for the exam
 [target.'cfg(any(target_os = "android", all(unix, not(target_os = "macos"))))'.dependencies]
 freetype = { version = "0.2", default-features = false }
 
 [target.'cfg(target_os = "windows")'.dependencies]
 dwrote = "0.4"
 
 [target.'cfg(target_os = "macos")'.dependencies]
 core-graphics = "0.8.0"
-core-text = { version = "5.0.1", features = ["lion"] }
+core-text = { version = "6.1", default-features = false }
--- a/gfx/webrender/benches/coalesce.rs
+++ b/gfx/webrender/benches/coalesce.rs
@@ -1,19 +1,18 @@
 #![feature(test)]
 
 extern crate rand;
 extern crate test;
 extern crate webrender;
-extern crate webrender_traits;
 
 use rand::Rng;
 use test::Bencher;
 use webrender::TexturePage;
-use webrender_traits::{DeviceUintSize as Size};
+use webrender::api::{DeviceUintSize as Size};
 
 #[bench]
 fn bench_coalesce(b: &mut Bencher) {
     let mut rng = rand::thread_rng();
     let mut page = TexturePage::new_dummy(Size::new(10000, 10000));
     let mut test_page = TexturePage::new_dummy(Size::new(10000, 10000));
     while page.allocate(&Size::new(rng.gen_range(1, 100), rng.gen_range(1, 100))).is_some() {}
     b.iter(|| {
--- a/gfx/webrender/examples/animation.rs
+++ b/gfx/webrender/examples/animation.rs
@@ -1,52 +1,47 @@
 /* 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 webrender_traits;
 
 #[macro_use]
 extern crate lazy_static;
 
 #[path="common/boilerplate.rs"]
 mod boilerplate;
 
 use boilerplate::HandyDandyRectBuilder;
 use std::sync::Mutex;
-use webrender_traits::*;
+use webrender::api::*;
 
 // This example creates a 100x100 white rect and allows the user to move it
 // around by using the arrow keys. It does this by using the animation API.
 
 fn body(_api: &RenderApi,
         builder: &mut DisplayListBuilder,
         _pipeline_id: &PipelineId,
-        _layout_size: &LayoutSize)
-{
+        _layout_size: &LayoutSize) {
     // Create a 100x100 stacking context with an animatable transform property.
     // Note the magic "42" we use as the animation key. That is used to update
     // the transform in the keyboard event handler code.
     let bounds = (0,0).to(100, 100);
-    builder.push_stacking_context(webrender_traits::ScrollPolicy::Scrollable,
+    builder.push_stacking_context(ScrollPolicy::Scrollable,
                                   bounds,
                                   Some(PropertyBinding::Binding(PropertyBindingKey::new(42))),
                                   TransformStyle::Flat,
                                   None,
-                                  webrender_traits::MixBlendMode::Normal,
+                                  MixBlendMode::Normal,
                                   Vec::new());
 
     // Fill it with a white rect
-    let clip = builder.push_clip_region(&bounds, vec![], None);
-    builder.push_rect(bounds,
-                      clip,
-                      ColorF::new(1.0, 1.0, 1.0, 1.0));
+    builder.push_rect(bounds, None, ColorF::new(1.0, 1.0, 1.0, 1.0));
 
     builder.pop_stacking_context();
 }
 
 lazy_static! {
     static ref TRANSFORM: Mutex<LayoutTransform> = Mutex::new(LayoutTransform::identity());
 }
 
--- a/gfx/webrender/examples/basic.rs
+++ b/gfx/webrender/examples/basic.rs
@@ -2,31 +2,32 @@
  * 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 webrender_traits;
+
+#[macro_use]
+extern crate lazy_static;
+
+#[path="common/boilerplate.rs"]
+mod boilerplate;
 
 use app_units::Au;
-use gleam::gl;
+use boilerplate::HandyDandyRectBuilder;
+use euclid::vec2;
 use glutin::TouchPhase;
 use std::collections::HashMap;
-use std::env;
 use std::fs::File;
 use std::io::Read;
-use std::path::PathBuf;
-use webrender_traits::{ClipRegionToken, ColorF, DisplayListBuilder, Epoch, GlyphInstance};
-use webrender_traits::{DeviceIntPoint, DeviceUintSize, LayoutPoint, LayoutRect, LayoutSize};
-use webrender_traits::{ImageData, ImageDescriptor, ImageFormat};
-use webrender_traits::{PipelineId, RenderApi, TransformStyle, BoxShadowClipMode};
-use euclid::vec2;
+use std::sync::Mutex;
+use webrender::api::*;
 
 #[derive(Debug)]
 enum Gesture {
     None,
     Pan,
     Zoom,
 }
 
@@ -163,164 +164,80 @@ impl TouchState {
 
 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
 }
 
-struct Notifier {
-    window_proxy: glutin::WindowProxy,
-}
-
-impl Notifier {
-    fn new(window_proxy: glutin::WindowProxy) -> Notifier {
-        Notifier {
-            window_proxy: window_proxy,
-        }
-    }
-}
-
-impl webrender_traits::RenderNotifier for Notifier {
-    fn new_frame_ready(&mut self) {
-        #[cfg(not(target_os = "android"))]
-        self.window_proxy.wakeup_event_loop();
-    }
-
-    fn new_scroll_frame_ready(&mut self, _composite_needed: bool) {
-        #[cfg(not(target_os = "android"))]
-        self.window_proxy.wakeup_event_loop();
-    }
-}
-
-fn push_sub_clip(api: &RenderApi, builder: &mut DisplayListBuilder, bounds: &LayoutRect)
-                 -> ClipRegionToken {
-    let mask_image = api.generate_image_key();
-    api.add_image(mask_image,
-                  ImageDescriptor::new(2, 2, ImageFormat::A8, true),
-                  ImageData::new(vec![0, 80, 180, 255]),
-                  None);
-    let mask = webrender_traits::ImageMask {
-        image: mask_image,
-        rect: LayoutRect::new(LayoutPoint::new(75.0, 75.0), LayoutSize::new(100.0, 100.0)),
-        repeat: false,
-    };
-    let complex = webrender_traits::ComplexClipRegion::new(
-        LayoutRect::new(LayoutPoint::new(50.0, 50.0), LayoutSize::new(100.0, 100.0)),
-        webrender_traits::BorderRadius::uniform(20.0));
-
-    builder.push_clip_region(bounds, vec![complex], Some(mask))
+fn main() {
+    boilerplate::main_wrapper(body, event_handler, None);
 }
 
-
-fn main() {
-    let args: Vec<String> = env::args().collect();
-    let res_path = if args.len() > 1 {
-        Some(PathBuf::from(&args[1]))
-    } else {
-        None
-    };
-
-    let window = glutin::WindowBuilder::new()
-                .with_title("WebRender Sample")
-                .with_multitouch()
-                .with_gl(glutin::GlRequest::GlThenGles {
-                    opengl_version: (3, 2),
-                    opengles_version: (3, 0)
-                })
-                .build()
-                .unwrap();
-
-    unsafe {
-        window.make_current().ok();
-    }
-
-    let gl = match gl::GlType::default() {
-        gl::GlType::Gl => unsafe { gl::GlFns::load_with(|symbol| window.get_proc_address(symbol) as *const _) },
-        gl::GlType::Gles => unsafe { gl::GlesFns::load_with(|symbol| window.get_proc_address(symbol) as *const _) },
-    };
-
-    println!("OpenGL version {}", gl.get_string(gl::VERSION));
-    println!("Shader resource path: {:?}", res_path);
-
-    let (width, height) = window.get_inner_size_pixels().unwrap();
-
-    let opts = webrender::RendererOptions {
-        resource_override_path: res_path,
-        debug: true,
-        precache_shaders: true,
-        device_pixel_ratio: window.hidpi_factor(),
-        .. Default::default()
-    };
-
-    let size = DeviceUintSize::new(width, height);
-    let (mut renderer, sender) = webrender::renderer::Renderer::new(gl, opts, size).unwrap();
-    let api = sender.create_api();
-
-    let notifier = Box::new(Notifier::new(window.create_window_proxy()));
-    renderer.set_render_notifier(notifier);
-
-    let epoch = Epoch(0);
-    let root_background_color = ColorF::new(0.3, 0.0, 0.0, 1.0);
-
-    let pipeline_id = PipelineId(0, 0);
-    let layout_size = LayoutSize::new(width as f32, height as f32);
-    let mut builder = webrender_traits::DisplayListBuilder::new(pipeline_id, layout_size);
-
-    let bounds = LayoutRect::new(LayoutPoint::zero(), layout_size);
-    builder.push_stacking_context(webrender_traits::ScrollPolicy::Scrollable,
+fn body(api: &RenderApi,
+        builder: &mut DisplayListBuilder,
+        _pipeline_id: &PipelineId,
+        layout_size: &LayoutSize) {
+    let bounds = LayoutRect::new(LayoutPoint::zero(), *layout_size);
+    builder.push_stacking_context(ScrollPolicy::Scrollable,
                                   bounds,
                                   None,
                                   TransformStyle::Flat,
                                   None,
-                                  webrender_traits::MixBlendMode::Normal,
+                                  MixBlendMode::Normal,
                                   Vec::new());
 
-    let clip = push_sub_clip(&api, &mut builder, &bounds);
-    builder.push_rect(LayoutRect::new(LayoutPoint::new(100.0, 100.0), LayoutSize::new(100.0, 100.0)),
-                      clip,
-                      ColorF::new(0.0, 1.0, 0.0, 1.0));
+    let image_mask_key = api.generate_image_key();
+    api.add_image(image_mask_key,
+                  ImageDescriptor::new(2, 2, ImageFormat::A8, true),
+                  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));
+    let id = builder.define_clip(None, bounds, vec![complex], Some(mask));
+    builder.push_clip_id(id);
 
-    let clip = push_sub_clip(&api, &mut builder, &bounds);
-    builder.push_rect(LayoutRect::new(LayoutPoint::new(250.0, 100.0), LayoutSize::new(100.0, 100.0)),
-                      clip,
-                      ColorF::new(0.0, 1.0, 0.0, 1.0));
-    let border_side = webrender_traits::BorderSide {
+    let bounds = (100, 100).to(200, 200);
+    builder.push_rect(bounds, None, ColorF::new(0.0, 1.0, 0.0, 1.0));
+
+    let bounds = (250, 100).to(350, 200);
+    builder.push_rect(bounds, None, 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: webrender_traits::BorderStyle::Groove,
+        style: BorderStyle::Groove,
     };
-    let border_widths = webrender_traits::BorderWidths {
+    let border_widths = BorderWidths {
         top: 10.0,
         left: 10.0,
         bottom: 10.0,
         right: 10.0,
     };
-    let border_details = webrender_traits::BorderDetails::Normal(webrender_traits::NormalBorder {
+    let border_details = BorderDetails::Normal(NormalBorder {
         top: border_side,
         right: border_side,
         bottom: border_side,
         left: border_side,
-        radius: webrender_traits::BorderRadius::uniform(20.0),
+        radius: BorderRadius::uniform(20.0),
     });
 
-    let clip = push_sub_clip(&api, &mut builder, &bounds);
-    builder.push_border(LayoutRect::new(LayoutPoint::new(100.0, 100.0), LayoutSize::new(100.0, 100.0)),
-                        clip,
-                        border_widths,
-                        border_details);
+    let bounds = (100, 100).to(200, 200);
+    builder.push_border(bounds, None, border_widths, border_details);
 
 
     if false { // draw text?
         let font_key = api.generate_font_key();
         let font_bytes = load_file("res/FreeSans.ttf");
         api.add_raw_font(font_key, font_bytes, 0);
 
-        let text_bounds = LayoutRect::new(LayoutPoint::new(100.0, 200.0), LayoutSize::new(700.0, 300.0));
-
+        let text_bounds = (100, 200).by(700, 300);
         let glyphs = vec![
             GlyphInstance {
                 index: 48,
                 point: LayoutPoint::new(100.0, 100.0),
             },
             GlyphInstance {
                 index: 68,
                 point: LayoutPoint::new(150.0, 100.0),
@@ -362,96 +279,65 @@ fn main() {
                 point: LayoutPoint::new(600.0, 100.0),
             },
             GlyphInstance {
                 index: 17,
                 point: LayoutPoint::new(650.0, 100.0),
             },
         ];
 
-        let clip = builder.push_clip_region(&bounds, Vec::new(), None);
         builder.push_text(text_bounds,
-                          clip,
+                          None,
                           &glyphs,
                           font_key,
                           ColorF::new(1.0, 1.0, 0.0, 1.0),
                           Au::from_px(32),
                           0.0,
                           None);
     }
 
     if false { // draw box shadow?
-        let rect = LayoutRect::new(LayoutPoint::new(0.0, 0.0), LayoutSize::new(0.0, 0.0));
-        let simple_box_bounds = LayoutRect::new(LayoutPoint::new(20.0, 200.0),
-                                                LayoutSize::new(50.0, 50.0));
+        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 full_screen_clip = builder.push_clip_region(&bounds, Vec::new(), None);
 
         builder.push_box_shadow(rect,
-                                full_screen_clip,
+                                Some(LocalClip::from(bounds)),
                                 simple_box_bounds,
                                 offset,
                                 color,
                                 blur_radius,
                                 spread_radius,
                                 simple_border_radius,
                                 box_shadow_type);
     }
 
+    builder.pop_clip_id();
     builder.pop_stacking_context();
-
-    api.set_display_list(
-        Some(root_background_color),
-        epoch,
-        LayoutSize::new(width as f32, height as f32),
-        builder.finalize(),
-        true);
-    api.set_root_pipeline(pipeline_id);
-    api.generate_frame(None);
-
-    let mut touch_state = TouchState::new();
+}
 
-    'outer: for event in window.wait_events() {
-        let mut events = Vec::new();
-        events.push(event);
-
-        for event in window.poll_events() {
-            events.push(event);
-        }
+lazy_static! {
+    static ref TOUCH_STATE: Mutex<TouchState> = Mutex::new(TouchState::new());
+}
 
-        for event in events {
-            match event {
-                glutin::Event::Closed |
-                glutin::Event::KeyboardInput(_, _, Some(glutin::VirtualKeyCode::Escape)) |
-                glutin::Event::KeyboardInput(_, _, Some(glutin::VirtualKeyCode::Q)) => break 'outer,
-                glutin::Event::KeyboardInput(glutin::ElementState::Pressed,
-                                             _, Some(glutin::VirtualKeyCode::P)) => {
-                    let enable_profiler = !renderer.get_profiler_enabled();
-                    renderer.set_profiler_enabled(enable_profiler);
+fn event_handler(event: &glutin::Event, api: &RenderApi) {
+    match *event {
+        glutin::Event::Touch(touch) => {
+            match TOUCH_STATE.lock().unwrap().handle_event(touch) {
+                TouchResult::Pan(pan) => {
+                    api.set_pan(pan);
                     api.generate_frame(None);
                 }
-                glutin::Event::Touch(touch) => {
-                    match touch_state.handle_event(touch) {
-                        TouchResult::Pan(pan) => {
-                            api.set_pan(pan);
-                            api.generate_frame(None);
-                        }
-                        TouchResult::Zoom(zoom) => {
-                            api.set_pinch_zoom(webrender_traits::ZoomFactor::new(zoom));
-                            api.generate_frame(None);
-                        }
-                        TouchResult::None => {}
-                    }
+                TouchResult::Zoom(zoom) => {
+                    api.set_pinch_zoom(ZoomFactor::new(zoom));
+                    api.generate_frame(None);
                 }
-                _ => ()
+                TouchResult::None => {}
             }
         }
-
-        renderer.update();
-        renderer.render(DeviceUintSize::new(width, height));
-        window.swap_buffers().ok();
+        _ => ()
     }
 }
--- a/gfx/webrender/examples/blob.rs
+++ b/gfx/webrender/examples/blob.rs
@@ -2,60 +2,59 @@
  * 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 webrender_traits;
 extern crate rayon;
 
 #[path="common/boilerplate.rs"]
 mod boilerplate;
 
 use boilerplate::HandyDandyRectBuilder;
 use rayon::ThreadPool;
 use rayon::Configuration as ThreadPoolConfig;
 use std::collections::HashMap;
 use std::collections::hash_map::Entry;
 use std::sync::Arc;
 use std::sync::mpsc::{channel, Sender, Receiver};
-use webrender_traits as wt;
+use webrender::api;
 
 // 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 = wt::ColorU;
+type ImageRenderingCommands = api::ColorU;
 
 // Serialize/deserialze the blob.
 // Ror real usecases you should probably use serde rather than doing it by hand.
 
-fn serialize_blob(color: wt::ColorU) -> Vec<u8> {
+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(wt::ColorU::new(r, g, b, a)),
-        (Some(&a), None, None, None) => Ok(wt::ColorU::new(a, a, a, a)),
+        (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: &wt::BlobImageDescriptor,
-    tile: Option<wt::TileOffset>,
-) -> wt::BlobImageResult {
+   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.width * descriptor.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.
@@ -72,97 +71,97 @@ fn render_blob(
             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 cherkerboard pattern
             let tc = if tile_checker { 0 } else { (1 - checker) * 40 };
 
             match descriptor.format {
-                wt::ImageFormat::BGRA8 => {
+                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);
                 }
-                wt::ImageFormat::A8 => {
+                api::ImageFormat::A8 => {
                     texels.push(color.a * checker + tc);
                 }
                 _ => {
-                    return Err(wt::BlobImageError::Other(format!(
+                    return Err(api::BlobImageError::Other(format!(
                         "Usupported image format {:?}",
                         descriptor.format
                     )));
                 }
             }
         }
     }
 
-    Ok(wt::RasterizedBlobImage {
+    Ok(api::RasterizedBlobImage {
         data: texels,
         width: descriptor.width,
         height: descriptor.height,
     })
 }
 
 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<(wt::BlobImageRequest, wt::BlobImageResult)>,
-    rx: Receiver<(wt::BlobImageRequest, wt::BlobImageResult)>,
+    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<wt::ImageKey, Arc<ImageRenderingCommands>>,
+    image_cmds: HashMap<api::ImageKey, Arc<ImageRenderingCommands>>,
 
     // The images rendered in the current frame (not kept here between frames).
-    rendered_images: HashMap<wt::BlobImageRequest, Option<wt::BlobImageResult>>,
+    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: workers,
-            tx: tx,
-            rx: rx,
+            workers,
+            tx,
+            rx,
         }
     }
 }
 
-impl wt::BlobImageRenderer for CheckerboardRenderer {
-    fn add(&mut self, key: wt::ImageKey, cmds: wt::BlobImageData, _: Option<wt::TileSize>) {
+impl api::BlobImageRenderer for CheckerboardRenderer {
+    fn add(&mut self, key: api::ImageKey, cmds: api::BlobImageData, _: Option<api::TileSize>) {
         self.image_cmds.insert(key, Arc::new(deserialize_blob(&cmds[..]).unwrap()));
     }
 
-    fn update(&mut self, key: wt::ImageKey, cmds: wt::BlobImageData) {
+    fn update(&mut self, key: api::ImageKey, cmds: api::BlobImageData) {
         // 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: wt::ImageKey) {
+    fn delete(&mut self, key: api::ImageKey) {
         self.image_cmds.remove(&key);
     }
 
     fn request(&mut self,
-               resources: &wt::BlobImageResources,
-               request: wt::BlobImageRequest,
-               descriptor: &wt::BlobImageDescriptor,
-               _dirty_rect: Option<wt::DeviceUintRect>) {
+               _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();
@@ -174,25 +173,25 @@ impl wt::BlobImageRenderer for Checkerbo
 
         // 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 receing their result in resolve!
         self.rendered_images.insert(request, None);
     }
 
-    fn resolve(&mut self, request: wt::BlobImageRequest) -> wt::BlobImageResult {
+    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(wt::BlobImageError::InvalidKey);
+                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();
                 }
             }
@@ -203,76 +202,73 @@ impl wt::BlobImageRenderer for Checkerbo
             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(wt::BlobImageError::Other("Channel closed".into()))
+        Err(api::BlobImageError::Other("Channel closed".into()))
     }
-    fn delete_font(&mut self, font: wt::FontKey) {}
+    fn delete_font(&mut self, _font: api::FontKey) { }
 }
 
-fn body(api: &wt::RenderApi,
-        builder: &mut wt::DisplayListBuilder,
-        _pipeline_id: &wt::PipelineId,
-        layout_size: &wt::LayoutSize)
-{
+fn body(api: &api::RenderApi,
+        builder: &mut api::DisplayListBuilder,
+        _pipeline_id: &api::PipelineId,
+        layout_size: &api::LayoutSize) {
     let blob_img1 = api.generate_image_key();
     api.add_image(
         blob_img1,
-        wt::ImageDescriptor::new(500, 500, wt::ImageFormat::BGRA8, true),
-        wt::ImageData::new_blob_image(serialize_blob(wt::ColorU::new(50, 50, 150, 255))),
+        api::ImageDescriptor::new(500, 500, api::ImageFormat::BGRA8, true),
+        api::ImageData::new_blob_image(serialize_blob(api::ColorU::new(50, 50, 150, 255))),
         Some(128),
     );
 
     let blob_img2 = api.generate_image_key();
     api.add_image(
         blob_img2,
-        wt::ImageDescriptor::new(200, 200, wt::ImageFormat::BGRA8, true),
-        wt::ImageData::new_blob_image(serialize_blob(wt::ColorU::new(50, 150, 50, 255))),
+        api::ImageDescriptor::new(200, 200, api::ImageFormat::BGRA8, true),
+        api::ImageData::new_blob_image(serialize_blob(api::ColorU::new(50, 150, 50, 255))),
         None,
     );
 
-    let bounds = wt::LayoutRect::new(wt::LayoutPoint::zero(), *layout_size);
-    builder.push_stacking_context(wt::ScrollPolicy::Scrollable,
+    let bounds = api::LayoutRect::new(api::LayoutPoint::zero(), *layout_size);
+    builder.push_stacking_context(api::ScrollPolicy::Scrollable,
                                   bounds,
                                   None,
-                                  wt::TransformStyle::Flat,
+                                  api::TransformStyle::Flat,
                                   None,
-                                  wt::MixBlendMode::Normal,
+                                  api::MixBlendMode::Normal,
                                   Vec::new());
 
-    let clip = builder.push_clip_region(&bounds, vec![], None);
     builder.push_image(
         (30, 30).by(500, 500),
-        clip,
-        wt::LayoutSize::new(500.0, 500.0),
-        wt::LayoutSize::new(0.0, 0.0),
-        wt::ImageRendering::Auto,
+        Some(api::LocalClip::from(bounds)),
+        api::LayoutSize::new(500.0, 500.0),
+        api::LayoutSize::new(0.0, 0.0),
+        api::ImageRendering::Auto,
         blob_img1,
     );
 
-    let clip = builder.push_clip_region(&bounds, vec![], None);
     builder.push_image(
         (600, 600).by(200, 200),
-        clip,
-        wt::LayoutSize::new(200.0, 200.0),
-        wt::LayoutSize::new(0.0, 0.0),
-        wt::ImageRendering::Auto,
+        Some(api::LocalClip::from(bounds)),
+        api::LayoutSize::new(200.0, 200.0),
+        api::LayoutSize::new(0.0, 0.0),
+        api::ImageRendering::Auto,
         blob_img2,
     );
 
     builder.pop_stacking_context();
 }
 
 fn event_handler(_event: &glutin::Event,
-                 _api: &wt::RenderApi)
+                 _api: &api::RenderApi)
 {
 }
 
 fn main() {
     let worker_config = ThreadPoolConfig::new().thread_name(|idx|{
         format!("WebRender:Worker#{}", idx)
     });
 
--- a/gfx/webrender/examples/common/boilerplate.rs
+++ b/gfx/webrender/examples/common/boilerplate.rs
@@ -2,26 +2,26 @@
  * 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 gleam::gl;
 use glutin;
 use std::env;
 use std::path::PathBuf;
 use webrender;
-use webrender_traits::*;
+use webrender::api::*;
 
 struct Notifier {
     window_proxy: glutin::WindowProxy,
 }
 
 impl Notifier {
     fn new(window_proxy: glutin::WindowProxy) -> Notifier {
         Notifier {
-            window_proxy: window_proxy,
+            window_proxy,
         }
     }
 }
 
 impl RenderNotifier for Notifier {
     fn new_frame_ready(&mut self) {
         #[cfg(not(target_os = "android"))]
         self.window_proxy.wakeup_event_loop();
--- a/gfx/webrender/examples/nested_display_list.rs
+++ b/gfx/webrender/examples/nested_display_list.rs
@@ -1,82 +1,81 @@
 /* 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 webrender_traits;
 
 #[macro_use]
 extern crate lazy_static;
 
 #[path="common/boilerplate.rs"]
 mod boilerplate;
 
 use boilerplate::HandyDandyRectBuilder;
 use std::sync::Mutex;
-use webrender_traits::*;
+use webrender::api::*;
 
 fn body(_api: &RenderApi,
         builder: &mut DisplayListBuilder,
         pipeline_id: &PipelineId,
-        layout_size: &LayoutSize)
-{
+        layout_size: &LayoutSize) {
     let bounds = LayoutRect::new(LayoutPoint::zero(), *layout_size);
-    builder.push_stacking_context(webrender_traits::ScrollPolicy::Scrollable,
+    builder.push_stacking_context(ScrollPolicy::Scrollable,
                                   bounds,
                                   None,
                                   TransformStyle::Flat,
                                   None,
-                                  webrender_traits::MixBlendMode::Normal,
+                                  MixBlendMode::Normal,
                                   Vec::new());
 
     let outer_scroll_frame_rect = (100, 100).to(600, 400);
-    let token = builder.push_clip_region(&outer_scroll_frame_rect, vec![], None);
-    builder.push_rect(outer_scroll_frame_rect,
-                      token, ColorF::new(1.0, 1.0, 1.0, 1.0));
-    let token = builder.push_clip_region(&outer_scroll_frame_rect, vec![], None);
-    let nested_clip_id = builder.define_clip((100, 100).to(1000, 1000), token, None);
+    builder.push_rect(outer_scroll_frame_rect, None, ColorF::new(1.0, 1.0, 1.0, 1.0));
+
+    let nested_clip_id = builder.define_scroll_frame(None,
+                                                     (100, 100).to(1000, 1000),
+                                                     outer_scroll_frame_rect,
+                                                     vec![],
+                                                     None);
     builder.push_clip_id(nested_clip_id);
 
-    let mut builder2 = webrender_traits::DisplayListBuilder::new(*pipeline_id, *layout_size);
-    let mut builder3 = webrender_traits::DisplayListBuilder::new(*pipeline_id, *layout_size);
+    let mut builder2 = DisplayListBuilder::new(*pipeline_id, *layout_size);
+    let mut builder3 = DisplayListBuilder::new(*pipeline_id, *layout_size);
 
     let rect = (110, 110).to(210, 210);
-    let token = builder3.push_clip_region(&rect, vec![], None);
-    builder3.push_rect(rect, token, ColorF::new(0.0, 1.0, 0.0, 1.0));
+    builder3.push_rect(rect, None, ColorF::new(0.0, 1.0, 0.0, 1.0));
 
     // A fixed position rectangle should be fixed to the reference frame that starts
     // in the outer display list.
-    builder3.push_stacking_context(webrender_traits::ScrollPolicy::Fixed,
+    builder3.push_stacking_context(ScrollPolicy::Fixed,
                                   (220, 110).to(320, 210),
                                   None,
                                   TransformStyle::Flat,
                                   None,
-                                  webrender_traits::MixBlendMode::Normal,
+                                  MixBlendMode::Normal,
                                   Vec::new());
     let rect = (0, 0).to(100, 100);
-    let token = builder3.push_clip_region(&rect, vec![], None);
-    builder3.push_rect(rect, token, ColorF::new(0.0, 1.0, 0.0, 1.0));
+    builder3.push_rect(rect, None, ColorF::new(0.0, 1.0, 0.0, 1.0));
     builder3.pop_stacking_context();
 
     // Now we push an inner scroll frame that should have the same id as the outer one,
     // but the WebRender nested display list replacement code should convert it into
     // a unique ClipId.
     let inner_scroll_frame_rect = (330, 110).to(530, 360);
-    let token = builder3.push_clip_region(&inner_scroll_frame_rect, vec![], None);
-    builder3.push_rect(inner_scroll_frame_rect, token, ColorF::new(1.0, 0.0, 1.0, 0.5));
-    let token = builder3.push_clip_region(&inner_scroll_frame_rect, vec![], None);
-    let inner_nested_clip_id = builder3.define_clip((330, 110).to(2000, 2000), token, None);
+    builder3.push_rect(inner_scroll_frame_rect, None, ColorF::new(1.0, 0.0, 1.0, 0.5));
+    let inner_nested_clip_id = builder3.define_scroll_frame(None,
+                                                            (330, 110).to(2000, 2000),
+                                                            inner_scroll_frame_rect,
+                                                            vec![],
+                                                            None);
     builder3.push_clip_id(inner_nested_clip_id);
     let rect = (340, 120).to(440, 220);
-    let token = builder3.push_clip_region(&rect, vec![], None);
-    builder3.push_rect(rect, token, ColorF::new(0.0, 1.0, 0.0, 1.0));
+    builder3.push_rect(rect, None, ColorF::new(0.0, 1.0, 0.0, 1.0));
     builder3.pop_clip_id();
 
     let (_, _, built_list) = builder3.finalize();
     builder2.push_nested_display_list(&built_list);
     let (_, _, built_list) = builder2.finalize();
     builder.push_nested_display_list(&built_list);
 
     builder.pop_clip_id();
--- a/gfx/webrender/examples/scrolling.rs
+++ b/gfx/webrender/examples/scrolling.rs
@@ -1,119 +1,108 @@
 /* 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 webrender_traits;
 
 #[macro_use]
 extern crate lazy_static;
 
 #[path="common/boilerplate.rs"]
 mod boilerplate;
 
 use boilerplate::HandyDandyRectBuilder;
 use std::sync::Mutex;
-use webrender_traits::*;
+use webrender::api::*;
 
 fn body(_api: &RenderApi,
         builder: &mut DisplayListBuilder,
-        pipeline_id: &PipelineId,
-        layout_size: &LayoutSize)
-{
+        _pipeline_id: &PipelineId,
+        layout_size: &LayoutSize) {
     let bounds = LayoutRect::new(LayoutPoint::zero(), *layout_size);
-    builder.push_stacking_context(webrender_traits::ScrollPolicy::Scrollable,
+    builder.push_stacking_context(ScrollPolicy::Scrollable,
                                   bounds,
                                   None,
                                   TransformStyle::Flat,
                                   None,
-                                  webrender_traits::MixBlendMode::Normal,
+                                  MixBlendMode::Normal,
                                   Vec::new());
 
     if true {   // scrolling and clips stuff
         // let's make a scrollbox
         let scrollbox = (0, 0).to(300, 400);
-        builder.push_stacking_context(webrender_traits::ScrollPolicy::Scrollable,
+        builder.push_stacking_context(ScrollPolicy::Scrollable,
                                       LayoutRect::new(LayoutPoint::new(10.0, 10.0),
                                                       LayoutSize::zero()),
                                       None,
                                       TransformStyle::Flat,
                                       None,
-                                      webrender_traits::MixBlendMode::Normal,
+                                      MixBlendMode::Normal,
                                       Vec::new());
         // set the scrolling clip
-        let clip = builder.push_clip_region(&scrollbox, vec![], None);
-        let clip_id = builder.define_clip((0, 0).to(1000, 1000),
-                                          clip,
-                                          Some(ClipId::new(42, *pipeline_id)));
+        let clip_id = builder.define_scroll_frame(None,
+                                                  (0, 0).by(1000, 1000),
+                                                  scrollbox,
+                                                  vec![],
+                                                  None);
         builder.push_clip_id(clip_id);
+
         // now put some content into it.
         // start with a white background
-        let clip = builder.push_clip_region(&(0, 0).to(1000, 1000), vec![], None);
-        builder.push_rect((0, 0).to(500, 500),
-                          clip,
-                          ColorF::new(1.0, 1.0, 1.0, 1.0));
+        builder.push_rect((0, 0).to(1000, 1000), None, ColorF::new(1.0, 1.0, 1.0, 1.0));
+
         // let's make a 50x50 blue square as a visual reference
-        let clip = builder.push_clip_region(&(0, 0).to(50, 50), vec![], None);
-        builder.push_rect((0, 0).to(50, 50),
-                          clip,
-                          ColorF::new(0.0, 0.0, 1.0, 1.0));
+        builder.push_rect((0, 0).to(50, 50), None, 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 clip = builder.push_clip_region(&(60, 10).to(110, 60), vec![], None);
         builder.push_rect((50, 0).to(100, 50),
-                          clip,
+                          Some(LocalClip::from((60, 10).to(110, 60))),
                           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 clip = builder.push_clip_region(&(0, 100).to(200, 300), vec![], None);
-        let nested_clip_id = builder.define_clip((0, 100).to(300, 400),
-                                                 clip,
-                                                 Some(ClipId::new(43, *pipeline_id)));
+        let nested_clip_id = builder.define_scroll_frame(None,
+                                                         (0, 100).to(300, 400),
+                                                         (0, 100).to(200, 300),
+                                                         vec![],
+                                                         None);
         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 clip = builder.push_clip_region(&(-1000, -1000).to(5000, 5000), vec![], None);
-        builder.push_rect((-1000, -1000).to(5000, 5000),
-                          clip,
-                          ColorF::new(0.5, 0.5, 0.5, 1.0));
+        builder.push_rect((-1000, -1000).to(5000, 5000), None, 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 with WASD keys
-        let clip = builder.push_clip_region(&(0, 100).to(50, 150), vec![], None);
-        builder.push_rect((0, 100).to(50, 150),
-                          clip,
-                          ColorF::new(0.0, 1.0, 1.0, 1.0));
+        builder.push_rect((0, 100).to(50, 150), None, ColorF::new(0.0, 1.0, 1.0, 1.0));
+
         // just for good measure add another teal square in the bottom-right
         // corner of the nested scrollframe content, which can be scrolled into
         // view by the user
-        let clip = builder.push_clip_region(&(250, 350).to(300, 400), vec![], None);
-        builder.push_rect((250, 350).to(300, 400),
-                          clip,
-                          ColorF::new(0.0, 1.0, 1.0, 1.0));
+        builder.push_rect((250, 350).to(300, 400), None, 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();
 }
 
 lazy_static! {
     static ref CURSOR_POSITION: Mutex<WorldPoint> = Mutex::new(WorldPoint::zero());
 }
 
-fn event_handler(event: &glutin::Event,
-                 api: &RenderApi)
-{
+fn event_handler(event: &glutin::Event, api: &RenderApi) {
     match *event {
         glutin::Event::KeyboardInput(glutin::ElementState::Pressed, _, Some(key)) => {
             let offset = match key {
                  glutin::VirtualKeyCode::Down => (0.0, -10.0),
                  glutin::VirtualKeyCode::Up => (0.0, 10.0),
                  glutin::VirtualKeyCode::Right => (-10.0, 0.0),
                  glutin::VirtualKeyCode::Left => (10.0, 0.0),
                  _ => return,
--- a/gfx/webrender/examples/yuv.rs
+++ b/gfx/webrender/examples/yuv.rs
@@ -2,28 +2,27 @@
  * 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 webrender_traits;
+
+#[macro_use]
+extern crate lazy_static;
 
-use gleam::gl;
+#[path="common/boilerplate.rs"]
+mod boilerplate;
+
 use glutin::TouchPhase;
 use std::collections::HashMap;
-use std::env;
-use std::path::PathBuf;
-use webrender_traits::{ColorF, Epoch};
-use webrender_traits::{DeviceIntPoint, DeviceUintSize, LayoutPoint, LayoutRect, LayoutSize};
-use webrender_traits::{ImageData, ImageDescriptor, ImageFormat, ImageRendering};
-use webrender_traits::{PipelineId, TransformStyle};
-use webrender_traits::{YuvColorSpace, YuvData};
+use std::sync::Mutex;
+use webrender::api::*;
 
 #[derive(Debug)]
 enum Gesture {
     None,
     Pan,
     Zoom,
 }
 
@@ -153,102 +152,31 @@ impl TouchState {
                 self.current_gesture = Gesture::None;
             }
         }
 
         TouchResult::None
     }
 }
 
-struct Notifier {
-    window_proxy: glutin::WindowProxy,
-}
-
-impl Notifier {
-    fn new(window_proxy: glutin::WindowProxy) -> Notifier {
-        Notifier {
-            window_proxy: window_proxy,
-        }
-    }
-}
-
-impl webrender_traits::RenderNotifier for Notifier {
-    fn new_frame_ready(&mut self) {
-        #[cfg(not(target_os = "android"))]
-        self.window_proxy.wakeup_event_loop();
-    }
-
-    fn new_scroll_frame_ready(&mut self, _composite_needed: bool) {
-        #[cfg(not(target_os = "android"))]
-        self.window_proxy.wakeup_event_loop();
-    }
+fn main() {
+    boilerplate::main_wrapper(body, event_handler, None);
 }
 
-fn main() {
-    let args: Vec<String> = env::args().collect();
-    let res_path = if args.len() > 1 {
-        Some(PathBuf::from(&args[1]))
-    } else {
-        None
-    };
-
-    let window = glutin::WindowBuilder::new()
-                .with_title("WebRender Sample")
-                .with_multitouch()
-                .with_gl(glutin::GlRequest::GlThenGles {
-                    opengl_version: (3, 2),
-                    opengles_version: (3, 0)
-                })
-                .build()
-                .unwrap();
-
-    unsafe {
-        window.make_current().ok();
-    }
-
-    let gl = match gl::GlType::default() {
-        gl::GlType::Gl => unsafe { gl::GlFns::load_with(|symbol| window.get_proc_address(symbol) as *const _) },
-        gl::GlType::Gles => unsafe { gl::GlesFns::load_with(|symbol| window.get_proc_address(symbol) as *const _) },
-    };
-
-    println!("OpenGL version {}", gl.get_string(gl::VERSION));
-    println!("Shader resource path: {:?}", res_path);
-
-    let (width, height) = window.get_inner_size_pixels().unwrap();
-
-    let opts = webrender::RendererOptions {
-        resource_override_path: res_path,
-        debug: true,
-        precache_shaders: true,
-        blob_image_renderer: None,
-        device_pixel_ratio: window.hidpi_factor(),
-        .. Default::default()
-    };
-
-    let size = DeviceUintSize::new(width, height);
-    let (mut renderer, sender) = webrender::renderer::Renderer::new(gl, opts, size).unwrap();
-    let api = sender.create_api();
-
-    let notifier = Box::new(Notifier::new(window.create_window_proxy()));
-    renderer.set_render_notifier(notifier);
-
-    let epoch = Epoch(0);
-    let root_background_color = ColorF::new(0.3, 0.0, 0.0, 1.0);
-
-    let pipeline_id = PipelineId(0, 0);
-    let layout_size = LayoutSize::new(width as f32, height as f32);
-    let mut builder = webrender_traits::DisplayListBuilder::new(pipeline_id, layout_size);
-
-    let bounds = LayoutRect::new(LayoutPoint::zero(), layout_size);
-    builder.push_stacking_context(webrender_traits::ScrollPolicy::Scrollable,
+fn body(api: &RenderApi,
+        builder: &mut DisplayListBuilder,
+        _pipeline_id: &PipelineId,
+        layout_size: &LayoutSize) {
+    let bounds = LayoutRect::new(LayoutPoint::zero(), *layout_size);
+    builder.push_stacking_context(ScrollPolicy::Scrollable,
                                   bounds,
                                   None,
                                   TransformStyle::Flat,
                                   None,
-                                  webrender_traits::MixBlendMode::Normal,
+                                  MixBlendMode::Normal,
                                   Vec::new());
 
 
     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();
     api.add_image(
@@ -271,81 +199,50 @@ fn main() {
     );
     api.add_image(
         yuv_chanel3,
         ImageDescriptor::new(100, 100, ImageFormat::A8, true),
         ImageData::new(vec![127; 100 * 100]),
         None,
     );
 
-    let clip = builder.push_clip_region(&bounds, vec![], None);
     builder.push_yuv_image(
         LayoutRect::new(LayoutPoint::new(100.0, 0.0), LayoutSize::new(100.0, 100.0)),
-        clip,
+        Some(LocalClip::from(bounds)),
         YuvData::NV12(yuv_chanel1, yuv_chanel2),
         YuvColorSpace::Rec601,
         ImageRendering::Auto,
     );
 
-    let clip = builder.push_clip_region(&bounds, vec![], None);
     builder.push_yuv_image(
         LayoutRect::new(LayoutPoint::new(300.0, 0.0), LayoutSize::new(100.0, 100.0)),
-        clip,
+        Some(LocalClip::from(bounds)),
         YuvData::PlanarYCbCr(yuv_chanel1, yuv_chanel2_1, yuv_chanel3),
         YuvColorSpace::Rec601,
         ImageRendering::Auto,
     );
 
     builder.pop_stacking_context();
+}
 
-    api.set_display_list(
-        Some(root_background_color),
-        epoch,
-        LayoutSize::new(width as f32, height as f32),
-        builder.finalize(),
-        true);
-    api.set_root_pipeline(pipeline_id);
-    api.generate_frame(None);
+lazy_static! {
+    static ref TOUCH_STATE: Mutex<TouchState> = Mutex::new(TouchState::new());
+}
 
-    let mut touch_state = TouchState::new();
 
-    'outer: for event in window.wait_events() {
-        let mut events = Vec::new();
-        events.push(event);
-
-        for event in window.poll_events() {
-            events.push(event);
-        }
-
-        for event in events {
-            match event {
-                glutin::Event::Closed |
-                glutin::Event::KeyboardInput(_, _, Some(glutin::VirtualKeyCode::Escape)) |
-                glutin::Event::KeyboardInput(_, _, Some(glutin::VirtualKeyCode::Q)) => break 'outer,
-                glutin::Event::KeyboardInput(glutin::ElementState::Pressed,
-                                             _, Some(glutin::VirtualKeyCode::P)) => {
-                    let enable_profiler = !renderer.get_profiler_enabled();
-                    renderer.set_profiler_enabled(enable_profiler);
+fn event_handler(event: &glutin::Event, api: &RenderApi) {
+    match *event {
+        glutin::Event::Touch(touch) => {
+            match TOUCH_STATE.lock().unwrap().handle_event(touch) {
+                TouchResult::Pan(pan) => {
+                    api.set_pan(pan);
                     api.generate_frame(None);
                 }
-                glutin::Event::Touch(touch) => {
-                    match touch_state.handle_event(touch) {
-                        TouchResult::Pan(pan) => {
-                            api.set_pan(pan);
-                            api.generate_frame(None);
-                        }
-                        TouchResult::Zoom(zoom) => {
-                            api.set_pinch_zoom(webrender_traits::ZoomFactor::new(zoom));
-                            api.generate_frame(None);
-                        }
-                        TouchResult::None => {}
-                    }
+                TouchResult::Zoom(zoom) => {
+                    api.set_pinch_zoom(ZoomFactor::new(zoom));
+                    api.generate_frame(None);
                 }
-                _ => ()
+                TouchResult::None => {}
             }
         }
-
-        renderer.update();
-        renderer.render(DeviceUintSize::new(width, height));
-        window.swap_buffers().ok();
+        _ => ()
     }
 }
-
--- a/gfx/webrender/res/cs_clip_border.vs.glsl
+++ b/gfx/webrender/res/cs_clip_border.vs.glsl
@@ -17,42 +17,42 @@
 struct BorderCorner {
     RectWithSize rect;
     vec2 clip_center;
     int corner;
     int clip_mode;
 };
 
 BorderCorner fetch_border_corner(int index) {
-    vec4 data[2] = fetch_data_2(index);
+    vec4 data[2] = fetch_from_resource_cache_2(index);
     return BorderCorner(RectWithSize(data[0].xy, data[0].zw),
                         data[1].xy,
                         int(data[1].z),
                         int(data[1].w));
 }
 
 // Per-dash clip information.
 struct BorderClipDash {
     vec4 point_tangent_0;
     vec4 point_tangent_1;
 };
 
 BorderClipDash fetch_border_clip_dash(int index) {
-    vec4 data[2] = fetch_data_2(index);
+    vec4 data[2] = fetch_from_resource_cache_2(index);
     return BorderClipDash(data[0], data[1]);
 }
 
 // Per-dot clip information.
 struct BorderClipDot {
     vec3 center_radius;
 };
 
 BorderClipDot fetch_border_clip_dot(int index) {
-    vec4 data[2] = fetch_data_2(index);
-    return BorderClipDot(data[0].xyz);
+    vec4 data = fetch_from_resource_cache_1(index);
+    return BorderClipDot(data.xyz);
 }
 
 void main(void) {
     CacheClipInstance cci = fetch_clip_item(gl_InstanceID);
     ClipArea area = fetch_clip_area(cci.render_task_index);
     Layer layer = fetch_layer(cci.layer_index);
 
     // Fetch the header information for this corner clip.
@@ -80,25 +80,25 @@ void main(void) {
             case CORNER_BOTTOM_LEFT:
                 sign_modifier = vec2(-1.0, 1.0);
                 break;
         };
 
         switch (corner.clip_mode) {
             case CLIP_MODE_DASH: {
                 // Fetch the information about this particular dash.
-                BorderClipDash dash = fetch_border_clip_dash(cci.data_index + cci.segment_index);
+                BorderClipDash dash = fetch_border_clip_dash(cci.data_index + 2 + 2 * (cci.segment_index - 1));
                 vPoint_Tangent0 = dash.point_tangent_0 * sign_modifier.xyxy;
                 vPoint_Tangent1 = dash.point_tangent_1 * sign_modifier.xyxy;
                 vDotParams = vec3(0.0);
                 vAlphaMask = vec2(0.0, 1.0);
                 break;
             }
             case CLIP_MODE_DOT: {
-                BorderClipDot cdot = fetch_border_clip_dot(cci.data_index + cci.segment_index);
+                BorderClipDot cdot = fetch_border_clip_dot(cci.data_index + 2 + (cci.segment_index - 1));
                 vPoint_Tangent0 = vec4(1.0);
                 vPoint_Tangent1 = vec4(1.0);
                 vDotParams = vec3(cdot.center_radius.xy * sign_modifier, cdot.center_radius.z);
                 vAlphaMask = vec2(1.0, 1.0);
                 break;
             }
         }
     }
--- a/gfx/webrender/res/cs_clip_image.vs.glsl
+++ b/gfx/webrender/res/cs_clip_image.vs.glsl
@@ -3,18 +3,18 @@
  * 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/. */
 
 struct ImageMaskData {
     RectWithSize local_rect;
 };
 
 ImageMaskData fetch_mask_data(int index) {
-    vec4 data[2] = fetch_data_2(index);
-    return ImageMaskData(RectWithSize(data[0].xy, data[0].zw));
+    vec4 data = fetch_from_resource_cache_1(index);
+    return ImageMaskData(RectWithSize(data.xy, data.zw));
 }
 
 void main(void) {
     CacheClipInstance cci = fetch_clip_item(gl_InstanceID);
     ClipArea area = fetch_clip_area(cci.render_task_index);
     Layer layer = fetch_layer(cci.layer_index);
     ImageMaskData mask = fetch_mask_data(cci.data_index);
     RectWithSize local_rect = mask.local_rect;
--- a/gfx/webrender/res/cs_clip_rectangle.vs.glsl
+++ b/gfx/webrender/res/cs_clip_rectangle.vs.glsl
@@ -4,46 +4,46 @@
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 struct ClipRect {
     RectWithSize rect;
     vec4 mode;
 };
 
 ClipRect fetch_clip_rect(int index) {
-    vec4 data[2] = fetch_data_2(index);
+    vec4 data[2] = fetch_from_resource_cache_2(index);
     return ClipRect(RectWithSize(data[0].xy, data[0].zw), data[1]);
 }
 
 struct ClipCorner {
     RectWithSize rect;
     vec4 outer_inner_radius;
 };
 
 ClipCorner fetch_clip_corner(int index) {
-    vec4 data[2] = fetch_data_2(index);
+    vec4 data[2] = fetch_from_resource_cache_2(index);
     return ClipCorner(RectWithSize(data[0].xy, data[0].zw), data[1]);
 }
 
 struct ClipData {
     ClipRect rect;
     ClipCorner top_left;
     ClipCorner top_right;
     ClipCorner bottom_left;
     ClipCorner bottom_right;
 };
 
 ClipData fetch_clip(int index) {
     ClipData clip;
 
     clip.rect = fetch_clip_rect(index + 0);
-    clip.top_left = fetch_clip_corner(index + 1);
-    clip.top_right = fetch_clip_corner(index + 2);
-    clip.bottom_left = fetch_clip_corner(index + 3);
-    clip.bottom_right = fetch_clip_corner(index + 4);
+    clip.top_left = fetch_clip_corner(index + 2);
+    clip.top_right = fetch_clip_corner(index + 4);
+    clip.bottom_left = fetch_clip_corner(index + 6);
+    clip.bottom_right = fetch_clip_corner(index + 8);
 
     return clip;
 }
 
 void main(void) {
     CacheClipInstance cci = fetch_clip_item(gl_InstanceID);
     ClipArea area = fetch_clip_area(cci.render_task_index);
     Layer layer = fetch_layer(cci.layer_index);
--- a/gfx/webrender/res/cs_text_run.vs.glsl
+++ b/gfx/webrender/res/cs_text_run.vs.glsl
@@ -26,13 +26,14 @@ void main(void) {
 
     vec2 texture_size = vec2(textureSize(sColor0, 0));
     vec2 st0 = res.uv_rect.xy / texture_size;
     vec2 st1 = res.uv_rect.zw / texture_size;
 
     vec2 pos = mix(local_rect.xy,
                    local_rect.xy + local_rect.zw,
                    aPosition.xy);
-	vUv = mix(st0, st1, aPosition.xy);
-	vColor = text.color;
+
+    vUv = mix(st0, st1, aPosition.xy);
+    vColor = text.color;
 
     gl_Position = uTransform * vec4(pos, 0.0, 1.0);
 }
--- a/gfx/webrender/res/prim_shared.glsl
+++ b/gfx/webrender/res/prim_shared.glsl
@@ -31,17 +31,17 @@
 #define PST_BOTTOM_LEFT  6
 #define PST_LEFT         7
 
 #define BORDER_LEFT      0
 #define BORDER_TOP       1
 #define BORDER_RIGHT     2
 #define BORDER_BOTTOM    3
 
-// Border styles as defined in webrender_traits/types.rs
+// Border styles as defined in webrender_api/types.rs
 #define BORDER_STYLE_NONE         0
 #define BORDER_STYLE_SOLID        1
 #define BORDER_STYLE_DOUBLE       2
 #define BORDER_STYLE_DOTTED       3
 #define BORDER_STYLE_DASHED       4
 #define BORDER_STYLE_HIDDEN       5
 #define BORDER_STYLE_GROOVE       6
 #define BORDER_STYLE_RIDGE        7
@@ -135,35 +135,26 @@ vec4[2] fetch_from_resource_cache_2(int 
 #define VECS_PER_PRIM_HEADER        2
 #define VECS_PER_TEXT_RUN           1
 #define VECS_PER_GRADIENT           3
 #define VECS_PER_GRADIENT_STOP      2
 
 uniform HIGHP_SAMPLER_FLOAT sampler2D sLayers;
 uniform HIGHP_SAMPLER_FLOAT sampler2D sRenderTasks;
 
-uniform HIGHP_SAMPLER_FLOAT sampler2D sData32;
-
 // Instanced attributes
 in ivec4 aData0;
 in ivec4 aData1;
 
 // get_fetch_uv is a macro to work around a macOS Intel driver parsing bug.
 // TODO: convert back to a function once the driver issues are resolved, if ever.
 // https://github.com/servo/webrender/pull/623
 // https://github.com/servo/servo/issues/13953
 #define get_fetch_uv(i, vpi)  ivec2(vpi * (i % (WR_MAX_VERTEX_TEXTURE_WIDTH/vpi)), i / (WR_MAX_VERTEX_TEXTURE_WIDTH/vpi))
 
-vec4[2] fetch_data_2(int index) {
-    ivec2 uv = get_fetch_uv(index, 2);
-    return vec4[2](
-        texelFetchOffset(sData32, uv, 0, ivec2(0, 0)),
-        texelFetchOffset(sData32, uv, 0, ivec2(1, 0))
-    );
-}
 
 vec4[8] fetch_from_resource_cache_8(int address) {
     ivec2 uv = get_resource_cache_uv(address);
     return vec4[8](
         texelFetchOffset(sResourceCache, uv, 0, ivec2(0, 0)),
         texelFetchOffset(sResourceCache, uv, 0, ivec2(1, 0)),
         texelFetchOffset(sResourceCache, uv, 0, ivec2(2, 0)),
         texelFetchOffset(sResourceCache, uv, 0, ivec2(3, 0)),
--- a/gfx/webrender/src/border.rs
+++ b/gfx/webrender/src/border.rs
@@ -1,20 +1,21 @@
 /* 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::{BorderSide, BorderStyle, BorderWidths, ClipAndScrollInfo, ColorF, LayerPoint, LayerRect};
+use api::{LayerSize, LocalClip, NormalBorder};
 use ellipse::Ellipse;
+use gpu_cache::GpuDataRequest;
 use frame_builder::FrameBuilder;
-use mask_cache::{ClipSource};
-use prim_store::{BorderPrimitiveCpu, GpuBlock32, PrimitiveContainer};
+use mask_cache::ClipSource;
+use prim_store::{BorderPrimitiveCpu, PrimitiveContainer};
 use tiling::PrimitiveFlags;
 use util::{lerp, pack_as_float};
-use webrender_traits::{BorderSide, BorderStyle, BorderWidths, ClipAndScrollInfo, ClipRegion};
-use webrender_traits::{ColorF, LayerPoint, LayerRect, LayerSize, NormalBorder};
 
 #[repr(u8)]
 #[derive(Debug, Copy, Clone, PartialEq)]
 pub enum BorderCornerInstance {
     Single,     // Single instance needed - corner styles are same or similar.
     Double,     // Different corner styles. Draw two instances, one per style.
 }
 
@@ -74,17 +75,17 @@ impl BorderCornerKind {
                                              border_rect.size.height -
                                              size.height);
                 let clip_center = origin + LayerSize::new(size.width, 0.0);
                 (origin, clip_center)
             }
         };
         let clip_data = BorderCornerClipData {
             corner_rect: LayerRect::new(origin, size),
-            clip_center: clip_center,
+            clip_center,
             corner: pack_as_float(corner as u32),
             kind: pack_as_float(kind as u32),
         };
         BorderCornerKind::Mask(clip_data,
                                radius,
                                LayerSize::new(width0, width1),
                                kind)
     }
@@ -220,33 +221,33 @@ impl NormalBorderHelpers for NormalBorde
 }
 
 impl FrameBuilder {
     fn add_normal_border_primitive(&mut self,
                                    rect: &LayerRect,
                                    border: &NormalBorder,
                                    widths: &BorderWidths,
                                    clip_and_scroll: ClipAndScrollInfo,
-                                   clip_region: &ClipRegion,
+                                   local_clip: &LocalClip,
                                    corner_instances: [BorderCornerInstance; 4],
                                    extra_clips: &[ClipSource]) {
         let radius = &border.radius;
         let left = &border.left;
         let right = &border.right;
         let top = &border.top;
         let bottom = &border.bottom;
 
         // These colors are used during inset/outset scaling.
         let left_color      = left.border_color(1.0, 2.0/3.0, 0.3, 0.7);
         let top_color       = top.border_color(1.0, 2.0/3.0, 0.3, 0.7);
         let right_color     = right.border_color(2.0/3.0, 1.0, 0.7, 0.3);
         let bottom_color    = bottom.border_color(2.0/3.0, 1.0, 0.7, 0.3);
 
         let prim_cpu = BorderPrimitiveCpu {
-            corner_instances: corner_instances,
+            corner_instances,
 
             // TODO(gw): In the future, we will build these on demand
             //           from the deserialized display list, rather
             //           than creating it immediately.
             gpu_blocks: [
                 [ pack_as_float(left.style as u32),
                   pack_as_float(top.style as u32),
                   pack_as_float(right.style as u32),
@@ -267,31 +268,31 @@ impl FrameBuilder {
                   radius.bottom_right.height,
                   radius.bottom_left.width,
                   radius.bottom_left.height ].into(),
             ],
         };
 
         self.add_primitive(clip_and_scroll,
                            &rect,
-                           clip_region,
+                           local_clip,
                            extra_clips,
                            PrimitiveContainer::Border(prim_cpu));
     }
 
     // TODO(gw): This allows us to move border types over to the
     // simplified shader model one at a time. Once all borders
     // are converted, this can be removed, along with the complex
     // border code path.
     pub fn add_normal_border(&mut self,
                              rect: &LayerRect,
                              border: &NormalBorder,
                              widths: &BorderWidths,
                              clip_and_scroll: ClipAndScrollInfo,
-                             clip_region: &ClipRegion) {
+                             local_clip: &LocalClip) {
         // The border shader is quite expensive. For simple borders, we can just draw
         // the border with a few rectangles. This generally gives better batching, and
         // a GPU win in fragment shader time.
         // More importantly, the software (OSMesa) implementation we run tests on is
         // particularly slow at running our complex border shader, compared to the
         // rectangle shader. This has the effect of making some of our tests time
         // out more often on CI (the actual cause is simply too many Servo processes and
         // threads being run on CI at once).
@@ -361,44 +362,44 @@ impl FrameBuilder {
             let p1 = rect.bottom_right();
             let rect_width = rect.size.width;
             let rect_height = rect.size.height;
 
             // Add a solid rectangle for each visible edge/corner combination.
             if top_edge == BorderEdgeKind::Solid {
                 self.add_solid_rectangle(clip_and_scroll,
                                          &LayerRect::new(p0, LayerSize::new(rect_width, top_len)),
-                                         clip_region,
+                                         local_clip,
                                          &border.top.color,
                                          PrimitiveFlags::None);
             }
             if left_edge == BorderEdgeKind::Solid {
                 self.add_solid_rectangle(clip_and_scroll,
                                          &LayerRect::new(LayerPoint::new(p0.x, p0.y + top_len),
                                                          LayerSize::new(left_len,
                                                                         rect_height - top_len - bottom_len)),
-                                         clip_region,
+                                         local_clip,
                                          &border.left.color,
                                          PrimitiveFlags::None);
             }
             if right_edge == BorderEdgeKind::Solid {
                 self.add_solid_rectangle(clip_and_scroll,
                                          &LayerRect::new(LayerPoint::new(p1.x - right_len,
                                                                          p0.y + top_len),
                                                          LayerSize::new(right_len,
                                                                         rect_height - top_len - bottom_len)),
-                                         clip_region,
+                                         local_clip,
                                          &border.right.color,
                                          PrimitiveFlags::None);
             }
             if bottom_edge == BorderEdgeKind::Solid {
                 self.add_solid_rectangle(clip_and_scroll,
                                          &LayerRect::new(LayerPoint::new(p0.x, p1.y - bottom_len),
                                                          LayerSize::new(rect_width, bottom_len)),
-                                         clip_region,
+                                         local_clip,
                                          &border.bottom.color,
                                          PrimitiveFlags::None);
             }
         } else {
             // Create clip masks for border corners, if required.
             let mut extra_clips = Vec::new();
             let mut corner_instances = [BorderCornerInstance::Single; 4];
 
@@ -417,17 +418,17 @@ impl FrameBuilder {
                     _ => {}
                 }
             }
 
             self.add_normal_border_primitive(rect,
                                              border,
                                              widths,
                                              clip_and_scroll,
-                                             clip_region,
+                                             local_clip,
                                              corner_instances,
                                              &extra_clips);
         }
     }
 }
 
 pub trait BorderSideHelpers {
     fn border_color(&self,
@@ -530,48 +531,48 @@ impl BorderCornerClipSource {
 
                 // Add space for one extra dot since they are centered at the
                 // start of the arc.
                 (ellipse, 1 + max_dot_count.ceil() as usize)
             }
         };
 
         BorderCornerClipSource {
-            kind: kind,
-            corner_data: corner_data,
-            max_clip_count: max_clip_count,
+            kind,
+            corner_data,
+            max_clip_count,
             actual_clip_count: 0,
-            ellipse: ellipse,
-            widths: widths,
+            ellipse,
+            widths,
         }
     }
 
-    pub fn populate_gpu_data(&mut self, slice: &mut [GpuBlock32]) {
-        let (header, clips) = slice.split_first_mut().unwrap();
-        *header = self.corner_data.into();
+    pub fn write(&mut self, mut request: GpuDataRequest) {
+        self.corner_data.write(&mut request);
 
         match self.kind {
             BorderCornerClipKind::Dash => {
                 // Get the correct dash arc length.
                 self.actual_clip_count = self.max_clip_count;
                 let dash_arc_length = 0.5 * self.ellipse.total_arc_length / (self.actual_clip_count - 1) as f32;
                 let mut current_arc_length = -0.5 * dash_arc_length;
-                for dash_index in 0..self.actual_clip_count {
+                for _ in 0..self.actual_clip_count {
                     let arc_length0 = current_arc_length;
                     current_arc_length += dash_arc_length;
 
                     let arc_length1 = current_arc_length;
                     current_arc_length += dash_arc_length;
 
                     let dash_data = BorderCornerDashClipData::new(arc_length0,
                                                                   arc_length1,
                                                                   &self.ellipse);
+                    dash_data.write(&mut request);
+                }
 
-                    clips[dash_index] = dash_data.into();
-                }
+                assert_eq!(request.close(), 2 + 2 * self.actual_clip_count);
             }
             BorderCornerClipKind::Dot => {
                 let mut forward_dots = Vec::new();
                 let mut back_dots = Vec::new();
                 let mut leftover_arc_length = 0.0;
 
                 // Alternate between adding dots at the start and end of the
                 // ellipse arc. This ensures that we always end up with an exact
@@ -618,37 +619,36 @@ impl BorderCornerClipSource {
                         back_dots.push(dot);
                     }
                 }
 
                 // Now step through the dots, and distribute any extra
                 // leftover space on the arc between them evenly. Once
                 // the final arc position is determined, generate the correct
                 // arc positions and angles that get passed to the clip shader.
-                self.actual_clip_count = 0;
-                let dot_count = forward_dots.len() + back_dots.len();
-                let extra_space_per_dot = leftover_arc_length / (dot_count - 1) as f32;
+                self.actual_clip_count = forward_dots.len() + back_dots.len();
+                let extra_space_per_dot = leftover_arc_length / (self.actual_clip_count - 1) as f32;
 
                 for (i, dot) in forward_dots.iter().enumerate() {
                     let extra_dist = i as f32 * extra_space_per_dot;
                     let dot = BorderCornerDotClipData::new(dot.arc_pos + extra_dist,
                                                            0.5 * dot.diameter,
                                                            &self.ellipse);
-                    clips[self.actual_clip_count] = dot.into();
-                    self.actual_clip_count += 1;
+                    dot.write(&mut request);
                 }
 
                 for (i, dot) in back_dots.iter().enumerate() {
                     let extra_dist = i as f32 * extra_space_per_dot;
                     let dot = BorderCornerDotClipData::new(dot.arc_pos - extra_dist,
                                                            0.5 * dot.diameter,
                                                            &self.ellipse);
-                    clips[self.actual_clip_count] = dot.into();
-                    self.actual_clip_count += 1;
+                    dot.write(&mut request);
                 }
+
+                assert_eq!(request.close(), 2 + self.actual_clip_count);
             }
         }
     }
 }
 
 /// Represents the common GPU data for writing a
 /// clip mask for a border corner.
 #[derive(Debug, Copy, Clone, PartialEq)]
@@ -661,16 +661,23 @@ pub struct BorderCornerClipData {
     clip_center: LayerPoint,
     /// The shader needs to know which corner, to
     /// be able to flip the dash tangents to the
     /// right orientation.
     corner: f32,        // Of type BorderCorner enum
     kind: f32,          // Of type BorderCornerClipKind enum
 }
 
+impl BorderCornerClipData {
+    fn write(&self, request: &mut GpuDataRequest) {
+        request.push(self.corner_rect);
+        request.push([self.clip_center.x, self.clip_center.y, self.corner, self.kind]);
+    }
+}
+
 /// Represents the GPU data for drawing a single dash
 /// to a clip mask. A dash clip is defined by two lines.
 /// We store a point on the ellipse curve, and a tangent
 /// to that point, which allows for efficient line-distance
 /// calculations in the fragment shader.
 #[derive(Debug, Clone)]
 #[repr(C)]
 pub struct BorderCornerDashClipData {
@@ -692,49 +699,58 @@ impl BorderCornerDashClipData {
 
         BorderCornerDashClipData {
             point0: p0,
             tangent0: t0,
             point1: p1,
             tangent1: t1,
         }
     }
+
+    fn write(&self, request: &mut GpuDataRequest) {
+        request.push([self.point0.x, self.point0.y,
+                      self.tangent0.x, self.tangent0.y]);
+        request.push([self.point1.x, self.point1.y,
+                      self.tangent1.x, self.tangent1.y]);
+    }
 }
 
 /// Represents the GPU data for drawing a single dot
 /// to a clip mask.
 #[derive(Debug, Clone)]
 #[repr(C)]
 pub struct BorderCornerDotClipData {
     pub center: LayerPoint,
     pub radius: f32,
-    pub padding: [f32; 5],
 }
 
 impl BorderCornerDotClipData {
     pub fn new(arc_length: f32,
                radius: f32,
                ellipse: &Ellipse) -> BorderCornerDotClipData {
         let theta = ellipse.find_angle_for_arc_length(arc_length);
         let (center, _) = ellipse.get_point_and_tangent(theta);
 
         BorderCornerDotClipData {
-            center: center,
-            radius: radius,
-            padding: [0.0; 5],
+            center,
+            radius,
         }
     }
+
+    fn write(&self, request: &mut GpuDataRequest) {
+        request.push([self.center.x, self.center.y, self.radius, 0.0]);
+    }
 }
 
 #[derive(Copy, Clone, Debug)]
 struct DotInfo {
     arc_pos: f32,
     diameter: f32,
 }
 
 impl DotInfo {
     fn new(arc_pos: f32, diameter: f32) -> DotInfo {
         DotInfo {
-            arc_pos: arc_pos,
-            diameter: diameter,
+            arc_pos,
+            diameter,
         }
     }
 }
--- a/gfx/webrender/src/clip_scroll_node.rs
+++ b/gfx/webrender/src/clip_scroll_node.rs
@@ -1,74 +1,69 @@
 /* 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::{ClipId, DeviceIntRect, LayerPixel, LayerPoint, LayerRect, LayerSize};
+use api::{LayerToScrollTransform, LayerToWorldTransform, LayerVector2D, PipelineId};
+use api::{ScrollClamping, ScrollEventPhase, ScrollLocation, WorldPoint};
 use geometry::ray_intersects_rect;
-use mask_cache::{ClipSource, MaskCacheInfo, RegionMode};
-use prim_store::GpuBlock32;
-use renderer::VertexDataStore;
+use mask_cache::{ClipRegion, ClipSource, MaskCacheInfo};
 use spring::{DAMPING, STIFFNESS, Spring};
 use tiling::PackedLayerIndex;
-use util::TransformedRectKind;
-use webrender_traits::{ClipId, ClipRegion, DeviceIntRect, LayerPixel, LayerPoint, LayerRect};
-use webrender_traits::{LayerSize, LayerToScrollTransform, LayerToWorldTransform, PipelineId};
-use webrender_traits::{ScrollClamping, ScrollEventPhase, ScrollLayerRect, ScrollLocation};
-use webrender_traits::{WorldPoint, LayerVector2D};
-use webrender_traits::{as_scroll_parent_vector};
+use util::{MatrixHelpers, TransformedRectKind};
 
 #[cfg(target_os = "macos")]
 const CAN_OVERSCROLL: bool = true;
 
 #[cfg(not(target_os = "macos"))]
 const CAN_OVERSCROLL: bool = false;
 
 #[derive(Clone, Debug)]
 pub struct ClipInfo {
     /// The ClipSource for this node, which is used to generate mask_cache_info.
     pub clip_sources: Vec<ClipSource>,
 
     /// The MaskCacheInfo for this node, which is produced by processing the
     /// provided ClipSource.
-    pub mask_cache_info: Option<MaskCacheInfo>,
+    pub mask_cache_info: MaskCacheInfo,
 
     /// The packed layer index for this node, which is used to render a clip mask
     /// for it, if necessary.
     pub packed_layer_index: PackedLayerIndex,
 
     /// The final transformed rectangle of this clipping region for this node,
     /// which depends on the screen rectangle and the transformation of all of
     /// the parents.
     pub screen_bounding_rect: Option<(TransformedRectKind, DeviceIntRect)>,
+
+    /// The biggest final transformed rectangle that is completely inside the
+    /// clipping region for this node.
+    pub screen_inner_rect: DeviceIntRect,
+
+    /// A rectangle which defines the rough boundaries of this clip in reference
+    /// frame relative coordinates (with no scroll offsets).
+    pub clip_rect: LayerRect,
 }
 
 impl ClipInfo {
-    pub fn new(clip_region: &ClipRegion,
-               clip_store: &mut VertexDataStore<GpuBlock32>,
-               packed_layer_index: PackedLayerIndex,)
-               -> ClipInfo {
-        // We pass true here for the MaskCacheInfo because this type of
-        // mask needs an extra clip for the clip rectangle.
-        let clip_sources = vec![ClipSource::Region(clip_region.clone(), RegionMode::IncludeRect)];
+    pub fn new(clip_region: ClipRegion, packed_layer_index: PackedLayerIndex) -> ClipInfo {
+        let clip_rect = LayerRect::new(clip_region.origin, clip_region.main.size);
+        let clip_sources = vec![ClipSource::Region(clip_region)];
         ClipInfo {
-            mask_cache_info: MaskCacheInfo::new(&clip_sources, clip_store),
-            clip_sources: clip_sources,
-            packed_layer_index: packed_layer_index,
+            mask_cache_info: MaskCacheInfo::new(&clip_sources),
+            clip_sources,
+            packed_layer_index,
             screen_bounding_rect: None,
+            screen_inner_rect: DeviceIntRect::zero(),
+            clip_rect: clip_rect,
         }
     }
+}
 
-    pub fn is_masking(&self) -> bool {
-        match self.mask_cache_info {
-            Some(ref info) => info.is_masking(),
-            _ => false,
-        }
-    }
-
-}
 #[derive(Clone, Debug)]
 pub enum NodeType {
     /// Transform for this layer, relative to parent reference frame. A reference
     /// frame establishes a new coordinate space in the tree.
     ReferenceFrame(LayerToScrollTransform),
 
     /// Other nodes just do clipping, but no transformation.
     Clip(ClipInfo),
@@ -86,17 +81,20 @@ pub struct ClipScrollNode {
     /// Viewing rectangle in the coordinate system of the parent reference frame.
     pub local_viewport_rect: LayerRect,
 
     /// Clip rect of this node - typically the same as viewport rect, except
     /// in overscroll cases.
     pub local_clip_rect: LayerRect,
 
     /// Viewport rectangle clipped against parent layer(s) viewport rectangles.
-    /// This is in the coordinate system which starts at our origin.
+    /// This is in the coordinate system of the node origin.
+    /// Precisely, it combines the local clipping rectangles of all the parent
+    /// nodes on the way to the root, including those of `ClipRegion` rectangles.
+    /// The combined clip is lossy/concervative on `ReferenceFrame` nodes.
     pub combined_local_viewport_rect: LayerRect,
 
     /// World transform for the viewport rect itself. This is the parent
     /// reference frame transformation plus the scrolling offsets provided by
     /// the nodes in between the reference frame and this node.
     pub world_viewport_transform: LayerToWorldTransform,
 
     /// World transform for content transformed by this node.
@@ -118,86 +116,82 @@ pub struct ClipScrollNode {
 
     /// Whether or not this node is a reference frame.
     pub node_type: NodeType,
 }
 
 impl ClipScrollNode {
     pub fn new_scroll_frame(pipeline_id: PipelineId,
                             parent_id: ClipId,
-                            content_rect: &LayerRect,
-                            frame_rect: &LayerRect)
+                            frame_rect: &LayerRect,
+                            content_size: &LayerSize)
                             -> ClipScrollNode {
         ClipScrollNode {
-            content_size: content_rect.size,
+            content_size: *content_size,
             local_viewport_rect: *frame_rect,
             local_clip_rect: *frame_rect,
             combined_local_viewport_rect: LayerRect::zero(),
             world_viewport_transform: LayerToWorldTransform::identity(),
             world_content_transform: LayerToWorldTransform::identity(),
             reference_frame_relative_scroll_offset: LayerVector2D::zero(),
             parent: Some(parent_id),
             children: Vec::new(),
-            pipeline_id: pipeline_id,
+            pipeline_id,
             node_type: NodeType::ScrollFrame(ScrollingState::new()),
         }
     }
 
-    pub fn new(pipeline_id: PipelineId,
-               parent_id: ClipId,
-               content_rect: &LayerRect,
-               clip_rect: &LayerRect,
-               clip_info: ClipInfo)
-               -> ClipScrollNode {
-        // FIXME(mrobinson): We don't yet handle clipping rectangles that don't start at the origin
-        // of the node.
-        let local_viewport_rect = LayerRect::new(content_rect.origin, clip_rect.size);
+    pub fn new(pipeline_id: PipelineId, parent_id: ClipId, clip_info: ClipInfo) -> ClipScrollNode {
         ClipScrollNode {
-            content_size: content_rect.size,
-            local_viewport_rect: local_viewport_rect,
-            local_clip_rect: local_viewport_rect,
+            content_size: clip_info.clip_rect.size,
+            local_viewport_rect: clip_info.clip_rect,
+            local_clip_rect: clip_info.clip_rect,
             combined_local_viewport_rect: LayerRect::zero(),
             world_viewport_transform: LayerToWorldTransform::identity(),
             world_content_transform: LayerToWorldTransform::identity(),
             reference_frame_relative_scroll_offset: LayerVector2D::zero(),
             parent: Some(parent_id),
             children: Vec::new(),
-            pipeline_id: pipeline_id,
+            pipeline_id,
             node_type: NodeType::Clip(clip_info),
         }
     }
 
     pub fn new_reference_frame(parent_id: Option<ClipId>,
                                local_viewport_rect: &LayerRect,
                                content_size: LayerSize,
                                local_transform: &LayerToScrollTransform,
                                pipeline_id: PipelineId)
                                -> ClipScrollNode {
         ClipScrollNode {
-            content_size: content_size,
+            content_size,
             local_viewport_rect: *local_viewport_rect,
             local_clip_rect: *local_viewport_rect,
             combined_local_viewport_rect: LayerRect::zero(),
             world_viewport_transform: LayerToWorldTransform::identity(),
             world_content_transform: LayerToWorldTransform::identity(),
             reference_frame_relative_scroll_offset: LayerVector2D::zero(),
             parent: parent_id,
             children: Vec::new(),
-            pipeline_id: pipeline_id,
+            pipeline_id,
             node_type: NodeType::ReferenceFrame(*local_transform),
         }
     }
 
     pub fn add_child(&mut self, child: ClipId) {
         self.children.push(child);
     }
 
     pub fn finalize(&mut self, new_scrolling: &ScrollingState) {
         match self.node_type {
-            NodeType::ReferenceFrame(_) | NodeType::Clip(_) => (),
+            NodeType::ReferenceFrame(_) | NodeType::Clip(_) => {
+                if new_scrolling.offset != LayerVector2D::zero() {
+                    warn!("Tried to scroll a non-scroll node.");
+                }
+            }
             NodeType::ScrollFrame(ref mut scrolling) => *scrolling = *new_scrolling,
         }
     }
 
     pub fn set_scroll_origin(&mut self, origin: &LayerPoint, clamp: ScrollClamping) -> bool {
         let scrollable_height = self.scrollable_height();
         let scrollable_width = self.scrollable_width();
 
@@ -229,63 +223,40 @@ impl ClipScrollNode {
         scrolling.offset = new_offset;
         scrolling.bouncing_back = false;
         scrolling.started_bouncing_back = false;
         true
     }
 
     pub fn update_transform(&mut self,
                             parent_reference_frame_transform: &LayerToWorldTransform,
-                            parent_combined_viewport_rect: &ScrollLayerRect,
+                            parent_combined_viewport_rect: &LayerRect,
                             parent_scroll_offset: LayerVector2D,
                             parent_accumulated_scroll_offset: LayerVector2D) {
-        self.reference_frame_relative_scroll_offset = match self.node_type {
-            NodeType::ReferenceFrame(_) => LayerVector2D::zero(),
-            NodeType::Clip(_) | NodeType::ScrollFrame(..) => parent_accumulated_scroll_offset,
-        };
+
+        let scrolled_parent_combined_clip = parent_combined_viewport_rect
+            .translate(&-parent_scroll_offset);
 
-        let local_transform = match self.node_type {
-            NodeType::ReferenceFrame(transform) => transform,
-            NodeType::Clip(_) | NodeType::ScrollFrame(..) => LayerToScrollTransform::identity(),
-        };
-
-        let inv_transform = match local_transform.inverse() {
-            Some(transform) => transform,
-            None => {
-                // If a transform function causes the current transformation matrix of an object
-                // to be non-invertible, the object and its content do not get displayed.
-                self.combined_local_viewport_rect = LayerRect::zero();
-                return;
+        let (local_transform, combined_clip, reference_frame_scroll_offset) = match self.node_type {
+            NodeType::ReferenceFrame(transform) => {
+                let combined_clip = transform.with_destination::<LayerPixel>()
+                                             .inverse_rect_footprint(&scrolled_parent_combined_clip);
+                (transform, combined_clip, LayerVector2D::zero())
+            }
+            NodeType::Clip(_) | NodeType::ScrollFrame(_) => {
+                // Move the parent's viewport into the local space (of the node origin)
+                // and intersect with the local clip rectangle to get the local viewport.
+                let combined_clip = scrolled_parent_combined_clip.intersection(&self.local_clip_rect)
+                                                                 .unwrap_or(LayerRect::zero());
+                (LayerToScrollTransform::identity(), combined_clip, parent_accumulated_scroll_offset)
             }
         };
 
-        // We are trying to move the combined viewport rectangle of our parent nodes into the
-        // coordinate system of this node, so we must invert our transformation (only for
-        // reference frames) and then apply the scroll offset the parent layer. The combined
-        // local viewport rect doesn't include scrolling offsets so the only one that matters
-        // is the relative offset between us and the parent.
-        let parent_combined_viewport_in_local_space =
-            inv_transform.pre_translate(-as_scroll_parent_vector(&parent_scroll_offset).to_3d())
-                         .transform_rect(parent_combined_viewport_rect);
-
-        // Now that we have the combined viewport rectangle of the parent nodes in local space,
-        // we do the intersection and get our combined viewport rect in the coordinate system
-        // starting from our origin.
-        self.combined_local_viewport_rect = match self.node_type {
-            NodeType::Clip(_) | NodeType::ScrollFrame(..) => {
-                parent_combined_viewport_in_local_space.intersection(&self.local_clip_rect)
-                                                       .unwrap_or(LayerRect::zero())
-            }
-            NodeType::ReferenceFrame(_) => parent_combined_viewport_in_local_space,
-        };
-
-        // HACK: prevent the code above for non-AA transforms, it's incorrect.
-        if (local_transform.m13, local_transform.m23) != (0.0, 0.0) {
-            self.combined_local_viewport_rect = self.local_clip_rect;
-        }
+        self.combined_local_viewport_rect = combined_clip;
+        self.reference_frame_relative_scroll_offset = reference_frame_scroll_offset;
 
         // The transformation for this viewport in world coordinates is the transformation for
         // our parent reference frame, plus any accumulated scrolling offsets from nodes
         // between our reference frame and this node. For reference frames, we also include
         // whatever local transformation this reference frame provides. This can be combined
         // with the local_viewport_rect to get its position in world space.
         self.world_viewport_transform =
             parent_reference_frame_transform
--- a/gfx/webrender/src/clip_scroll_tree.rs
+++ b/gfx/webrender/src/clip_scroll_tree.rs
@@ -2,20 +2,19 @@
  * 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 clip_scroll_node::{ClipScrollNode, NodeType, ScrollingState};
 use fnv::FnvHasher;
 use print_tree::PrintTree;
 use std::collections::{HashMap, HashSet};
 use std::hash::BuildHasherDefault;
-use webrender_traits::{ClipId, LayerPoint, LayerRect, LayerToScrollTransform};
-use webrender_traits::{LayerToWorldTransform, PipelineId, ScrollClamping, ScrollEventPhase};
-use webrender_traits::{ScrollLayerRect, ScrollLayerState, ScrollLocation, WorldPoint};
-use webrender_traits::{as_scroll_parent_rect, LayerVector2D};
+use api::{ClipId, LayerPoint, LayerRect, LayerToScrollTransform};
+use api::{LayerToWorldTransform, PipelineId, ScrollClamping, ScrollEventPhase};
+use api::{LayerVector2D, ScrollLayerState, ScrollLocation, WorldPoint};
 
 pub type ScrollStates = HashMap<ClipId, ScrollingState, BuildHasherDefault<FnvHasher>>;
 
 pub struct ClipScrollTree {
     pub nodes: HashMap<ClipId, ClipScrollNode, BuildHasherDefault<FnvHasher>>,
     pub pending_scroll_offsets: HashMap<ClipId, (LayerPoint, ScrollClamping)>,
 
     /// The ClipId of the currently scrolling node. Used to allow the same
@@ -226,72 +225,74 @@ impl ClipScrollTree {
         if self.nodes.is_empty() {
             return;
         }
 
         let root_reference_frame_id = self.root_reference_frame_id();
         let root_viewport = self.nodes[&root_reference_frame_id].local_clip_rect;
         self.update_node_transform(root_reference_frame_id,
                                    &LayerToWorldTransform::create_translation(pan.x, pan.y, 0.0),
-                                   &as_scroll_parent_rect(&root_viewport),
+                                   &root_viewport,
                                    LayerVector2D::zero(),
                                    LayerVector2D::zero());
     }
 
     fn update_node_transform(&mut self,
                              layer_id: ClipId,
                              parent_reference_frame_transform: &LayerToWorldTransform,
-                             parent_viewport_rect: &ScrollLayerRect,
+                             parent_viewport_rect: &LayerRect,
                              parent_scroll_offset: LayerVector2D,
                              parent_accumulated_scroll_offset: LayerVector2D) {
         // TODO(gw): This is an ugly borrow check workaround to clone these.
         //           Restructure this to avoid the clones!
         let (reference_frame_transform,
-             viewport_rect,
+             combined_local_viewport_rect,
              scroll_offset,
              accumulated_scroll_offset,
              node_children) = {
-            match self.nodes.get_mut(&layer_id) {
-                Some(node) => {
-                    node.update_transform(parent_reference_frame_transform,
-                                          parent_viewport_rect,
-                                          parent_scroll_offset,
-                                          parent_accumulated_scroll_offset);
+
+            let mut node = match self.nodes.get_mut(&layer_id) {
+                Some(node) => node,
+                None => return,
+            };
+            node.update_transform(parent_reference_frame_transform,
+                                  parent_viewport_rect,
+                                  parent_scroll_offset,
+                                  parent_accumulated_scroll_offset);
 
-                    // The transformation we are passing is the transformation of the parent
-                    // reference frame and the offset is the accumulated offset of all the nodes
-                    // between us and the parent reference frame. If we are a reference frame,
-                    // we need to reset both these values.
-                    let (transform, offset, accumulated_scroll_offset) = match node.node_type {
-                        NodeType::ReferenceFrame(..) =>
-                            (node.world_viewport_transform,
-                             LayerVector2D::zero(),
-                             LayerVector2D::zero()),
-                        _ => {
-                            let scroll_offset = node.scroll_offset();
-                            (*parent_reference_frame_transform,
-                             scroll_offset,
-                             scroll_offset + parent_accumulated_scroll_offset)
-                        }
-                    };
+            // The transformation we are passing is the transformation of the parent
+            // reference frame and the offset is the accumulated offset of all the nodes
+            // between us and the parent reference frame. If we are a reference frame,
+            // we need to reset both these values.
+            let (reference_frame_transform, scroll_offset, accumulated_scroll_offset) = match node.node_type {
+                NodeType::ReferenceFrame(..) =>
+                    (node.world_viewport_transform,
+                     LayerVector2D::zero(),
+                     LayerVector2D::zero()),
+                NodeType::Clip(..) =>
+                    (*parent_reference_frame_transform,
+                     LayerVector2D::zero(),
+                     parent_accumulated_scroll_offset),
+                NodeType::ScrollFrame(ref scrolling) =>
+                    (*parent_reference_frame_transform,
+                     scrolling.offset,
+                     scrolling.offset + parent_accumulated_scroll_offset),
+            };
 
-                    (transform,
-                     as_scroll_parent_rect(&node.combined_local_viewport_rect),
-                     offset,
-                     accumulated_scroll_offset,
-                     node.children.clone())
-                }
-                None => return,
-            }
+            (reference_frame_transform,
+             node.combined_local_viewport_rect,
+             scroll_offset,
+             accumulated_scroll_offset,
+             node.children.clone())
         };
 
         for child_layer_id in node_children {
             self.update_node_transform(child_layer_id,
                                        &reference_frame_transform,
-                                       &viewport_rect,
+                                       &combined_local_viewport_rect,
                                        scroll_offset,
                                        accumulated_scroll_offset);
         }
     }
 
     pub fn tick_scrolling_bounce_animations(&mut self) {
         for (_, node) in &mut self.nodes {
             node.tick_scrolling_bounce_animation()
@@ -308,17 +309,16 @@ impl ClipScrollTree {
             };
 
             node.finalize(&scrolling_state);
 
             if let Some((pending_offset, clamping)) = self.pending_scroll_offsets.remove(clip_id) {
                 node.set_scroll_origin(&pending_offset, clamping);
             }
         }
-
     }
 
     pub fn generate_new_clip_id(&mut self, pipeline_id: PipelineId) -> ClipId {
         let new_id = ClipId::DynamicallyAddedNode(self.current_new_node_item, pipeline_id);
         self.current_new_node_item += 1;
         new_id
     }
 
@@ -360,16 +360,17 @@ impl ClipScrollTree {
 
     fn print_node(&self, id: &ClipId, pt: &mut PrintTree) {
         let node = self.nodes.get(id).unwrap();
 
         match node.node_type {
             NodeType::Clip(ref info) => {
                 pt.new_level("Clip".to_owned());
                 pt.add_item(format!("screen_bounding_rect: {:?}", info.screen_bounding_rect));
+                pt.add_item(format!("screen_inner_rect: {:?}", info.screen_inner_rect));
 
                 pt.new_level(format!("Clip Sources [{}]", info.clip_sources.len()));
                 for source in &info.clip_sources {
                     pt.add_item(format!("{:?}", source));
                 }
                 pt.end_level();
             }
             NodeType::ReferenceFrame(ref transform) => {
@@ -377,19 +378,19 @@ impl ClipScrollTree {
             }
             NodeType::ScrollFrame(scrolling_info) => {
                 pt.new_level(format!("ScrollFrame"));
                 pt.add_item(format!("scroll.offset: {:?}", scrolling_info.offset));
             }
         }
 
         pt.add_item(format!("content_size: {:?}", node.content_size));
-        pt.add_item(format!("combined_local_viewport_rect: {:?}", node.combined_local_viewport_rect));
         pt.add_item(format!("local_viewport_rect: {:?}", node.local_viewport_rect));
         pt.add_item(format!("local_clip_rect: {:?}", node.local_clip_rect));
+        pt.add_item(format!("combined_local_viewport_rect: {:?}", node.combined_local_viewport_rect));
         pt.add_item(format!("world_viewport_transform: {:?}", node.world_viewport_transform));
         pt.add_item(format!("world_content_transform: {:?}", node.world_content_transform));
 
         for child_id in &node.children {
             self.print_node(child_id, pt);
         }
 
         pt.end_level();
--- a/gfx/webrender/src/debug_colors.rs
+++ b/gfx/webrender/src/debug_colors.rs
@@ -1,15 +1,15 @@
 /* This Source Code Form is subject to the terms of the Mozilla Public
  * License, v. 2.0. If a copy of the MPL was not distributed with this
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 #![allow(dead_code)]
 
-use webrender_traits::ColorF;
+use api::ColorF;
 
 // A subset of the standard CSS colors, useful for defining GPU tag colors etc.
 
 pub const INDIGO: ColorF = ColorF { r: 0.294117647059, g: 0.0, b: 0.509803921569, a: 1.0 };
 pub const GOLD: ColorF = ColorF { r: 1.0, g: 0.843137254902, b: 0.0, a: 1.0 };
 pub const FIREBRICK: ColorF = ColorF { r: 0.698039215686, g: 0.133333333333, b: 0.133333333333, a: 1.0 };
 pub const INDIANRED: ColorF = ColorF { r: 0.803921568627, g: 0.360784313725, b: 0.360784313725, a: 1.0 };
 pub const YELLOW: ColorF = ColorF { r: 1.0, g: 1.0, b: 0.0, a: 1.0 };
--- a/gfx/webrender/src/debug_render.rs
+++ b/gfx/webrender/src/debug_render.rs
@@ -4,17 +4,17 @@
 
 use debug_font_data;
 use device::{Device, GpuMarker, ProgramId, VAOId, TextureId, VertexFormat};
 use device::{TextureFilter, VertexUsageHint, TextureTarget};
 use euclid::{Transform3D, Point2D, Size2D, Rect};
 use internal_types::{ORTHO_NEAR_PLANE, ORTHO_FAR_PLANE, TextureSampler};
 use internal_types::{DebugFontVertex, DebugColorVertex, RenderTargetMode, PackedColor};
 use std::f32;
-use webrender_traits::{ColorF, ImageFormat, DeviceUintSize};
+use api::{ColorF, ImageFormat, DeviceUintSize};
 
 pub struct DebugRenderer {
     font_vertices: Vec<DebugFontVertex>,
     font_indices: Vec<u32>,
     font_program_id: ProgramId,
     font_vao: VAOId,
     font_texture_id: TextureId,
 
@@ -43,24 +43,24 @@ impl DebugRenderer {
                             TextureFilter::Linear,
                             RenderTargetMode::None,
                             Some(&debug_font_data::FONT_BITMAP));
 
         DebugRenderer {
             font_vertices: Vec::new(),
             font_indices: Vec::new(),
             line_vertices: Vec::new(),
-            tri_vao: tri_vao,
+            tri_vao,
             tri_vertices: Vec::new(),
             tri_indices: Vec::new(),
-            font_program_id: font_program_id,
-            color_program_id: color_program_id,
-            font_vao: font_vao,
-            line_vao: line_vao,
-            font_texture_id: font_texture_id,
+            font_program_id,
+            color_program_id,
+            font_vao,
+            line_vao,
+            font_texture_id,
         }
     }
 
     pub fn line_height(&self) -> f32 {
         debug_font_data::FONT_SIZE as f32 * 1.1
     }
 
     pub fn add_text(&mut self,
--- a/gfx/webrender/src/device.rs
+++ b/gfx/webrender/src/device.rs
@@ -13,21 +13,22 @@ use super::shader_source;
 use std::collections::HashMap;
 use std::fs::File;
 use std::hash::BuildHasherDefault;
 use std::io::Read;
 use std::iter::repeat;
 use std::mem;
 use std::ops::Add;
 use std::path::PathBuf;
+use std::ptr;
 use std::rc::Rc;
 //use std::sync::mpsc::{channel, Sender};
 //use std::thread;
-use webrender_traits::{ColorF, ImageFormat};
-use webrender_traits::{DeviceIntPoint, DeviceIntRect, DeviceIntSize, DeviceUintSize};
+use api::{ColorF, ImageFormat};
+use api::{DeviceIntPoint, DeviceIntRect, DeviceIntSize, DeviceUintSize};
 
 #[derive(Debug, Copy, Clone, PartialEq, Ord, Eq, PartialOrd)]
 pub struct FrameId(usize);
 
 impl FrameId {
     pub fn new(value: usize) -> FrameId {
         FrameId(value)
     }
@@ -46,21 +47,21 @@ const GL_FORMAT_A: gl::GLuint = gl::RED;
 
 #[cfg(any(target_arch = "arm", target_arch = "aarch64"))]
 const GL_FORMAT_A: gl::GLuint = gl::ALPHA;
 
 const GL_FORMAT_BGRA_GL: gl::GLuint = gl::BGRA;
 
 const GL_FORMAT_BGRA_GLES: gl::GLuint = gl::BGRA_EXT;
 
-const SHADER_VERSION_GL: &'static str = "#version 150\n";
+const SHADER_VERSION_GL: &str = "#version 150\n";
 
-const SHADER_VERSION_GLES: &'static str = "#version 300 es\n";
+const SHADER_VERSION_GLES: &str = "#version 300 es\n";
 
-static SHADER_PREAMBLE: &'static str = "shared";
+static SHADER_PREAMBLE: &str = "shared";
 
 #[repr(u32)]
 pub enum DepthFunction {
     Less = gl::LESS,
     LessEqual = gl::LEQUAL,
 }
 
 #[derive(Copy, Clone, Debug, PartialEq)]
@@ -293,17 +294,17 @@ impl VertexFormat {
 
 impl TextureId {
     pub fn bind(&self, gl: &gl::Gl) {
         gl.bind_texture(self.target, self.name);
     }
 
     pub fn new(name: gl::GLuint, texture_target: TextureTarget) -> TextureId {
         TextureId {
-            name: name,
+            name,
             target: texture_target.to_gl_target(),
         }
     }
 
     pub fn invalid() -> TextureId {
         TextureId {
             name: 0,
             target: gl::TEXTURE_2D,
@@ -483,16 +484,19 @@ pub struct FBOId(gl::GLuint);
 pub struct VBOId(gl::GLuint);
 
 #[derive(PartialEq, Eq, Hash, Debug, Copy, Clone)]
 struct IBOId(gl::GLuint);
 
 #[derive(PartialEq, Eq, Hash, Debug, Copy, Clone)]
 pub struct UBOId(gl::GLuint);
 
+#[derive(PartialEq, Eq, Hash, Debug, Copy, Clone)]
+pub struct PBOId(gl::GLuint);
+
 const MAX_EVENTS_PER_FRAME: usize = 256;
 const MAX_PROFILE_FRAMES: usize = 4;
 
 pub trait NamedTag {
     fn get_label(&self) -> &str;
 }
 
 #[derive(Debug, Clone)]
@@ -512,28 +516,28 @@ pub struct GpuFrameProfile<T> {
 }
 
 impl<T> GpuFrameProfile<T> {
     fn new(gl: Rc<gl::Gl>) -> Self {
         match gl.get_type() {
             gl::GlType::Gl => {
                 let queries = gl.gen_queries(MAX_EVENTS_PER_FRAME as gl::GLint);
                 GpuFrameProfile {
-                    gl: gl,
-                    queries: queries,
+                    gl,
+                    queries,
                     samples: Vec::new(),
                     next_query: 0,
                     pending_query: 0,
                     frame_id: FrameId(0),
                     inside_frame: false,
                 }
             }
             gl::GlType::Gles => {
                 GpuFrameProfile {
-                    gl: gl,
+                    gl,
                     queries: Vec::new(),
                     samples: Vec::new(),
                     next_query: 0,
                     pending_query: 0,
                     frame_id: FrameId(0),
                     inside_frame: false,
                 }
             }
@@ -580,32 +584,32 @@ impl<T> GpuFrameProfile<T> {
         }
 
         let marker = GpuMarker::new(&self.gl, tag.get_label());
 
         if self.next_query < MAX_EVENTS_PER_FRAME {
             self.pending_query = self.queries[self.next_query];
             self.gl.begin_query(gl::TIME_ELAPSED, self.pending_query);
             self.samples.push(GpuSample {
-                tag: tag,
+                tag,
                 time_ns: 0,
             });
         } else {
             self.pending_query = 0;
         }
 
         self.next_query += 1;
         marker
     }
 
     fn add_marker_gles(&mut self, tag: T) -> GpuMarker
     where T: NamedTag {
         let marker = GpuMarker::new(&self.gl, tag.get_label());
         self.samples.push(GpuSample {
-            tag: tag,
+            tag,
             time_ns: 0,
         });
         marker
     }
 
     fn is_valid(&self) -> bool {
         self.next_query > 0 && self.next_query <= MAX_EVENTS_PER_FRAME
     }
@@ -810,17 +814,17 @@ impl FileWatcherThread {
                             }
                         }
                     }
                 }
             }
         });
 
         FileWatcherThread {
-            api_tx: api_tx,
+            api_tx,
         }
     }
 
     fn exit(&self) {
         self.api_tx.send(FileWatcherCmd::Exit).ok();
     }
 
     fn add_watch(&self, path: PathBuf) {
@@ -841,16 +845,17 @@ pub enum ShaderError {
 }
 
 pub struct Device {
     gl: Rc<gl::Gl>,
     // device state
     bound_textures: [TextureId; 16],
     bound_program: ProgramId,
     bound_vao: VAOId,
+    bound_pbo: PBOId,
     bound_read_fbo: FBOId,
     bound_draw_fbo: FBOId,
     default_read_fbo: gl::GLuint,
     default_draw_fbo: gl::GLuint,
     device_pixel_ratio: f32,
 
     // HW or API capabilties
     capabilities: Capabilities,
@@ -887,46 +892,47 @@ impl Device {
 
         let shader_preamble = get_shader_source(SHADER_PREAMBLE, &resource_override_path);
         //file_watcher.add_watch(resource_path);
 
         let max_ubo_size = gl.get_integer_v(gl::MAX_UNIFORM_BLOCK_SIZE) as usize;
         let max_texture_size = gl.get_integer_v(gl::MAX_TEXTURE_SIZE) as u32;
 
         Device {
-            gl: gl,
-            resource_override_path: resource_override_path,
+            gl,
+            resource_override_path,
             // This is initialized to 1 by default, but it is set
             // every frame by the call to begin_frame().
             device_pixel_ratio: 1.0,
             inside_frame: false,
 
             capabilities: Capabilities {
-                max_ubo_size: max_ubo_size,
+                max_ubo_size,
                 supports_multisampling: false, //TODO
             },
 
             bound_textures: [ TextureId::invalid(); 16 ],
             bound_program: ProgramId(0),
             bound_vao: VAOId(0),
+            bound_pbo: PBOId(0),
             bound_read_fbo: FBOId(0),
             bound_draw_fbo: FBOId(0),
             default_read_fbo: 0,
             default_draw_fbo: 0,
 
             textures: HashMap::default(),
             programs: HashMap::default(),
             vaos: HashMap::default(),
 
-            shader_preamble: shader_preamble,
+            shader_preamble,
 
             next_vao_id: 1,
             //file_watcher: file_watcher,
 
-            max_texture_size: max_texture_size,
+            max_texture_size,
             frame_id: FrameId(0),
         }
     }
 
     pub fn gl(&self) -> &gl::Gl {
         &*self.gl
     }
 
@@ -1001,16 +1007,18 @@ impl Device {
         self.clear_vertex_array();
 
         // FBO state
         self.bound_read_fbo = FBOId(self.default_read_fbo);
         self.bound_draw_fbo = FBOId(self.default_draw_fbo);
 
         // Pixel op state
         self.gl.pixel_store_i(gl::UNPACK_ALIGNMENT, 1);
+        self.bound_pbo = PBOId(0);
+        self.gl.bind_buffer(gl::PIXEL_UNPACK_BUFFER, 0);
 
         // Default is sampler 0, always
         self.gl.active_texture(gl::TEXTURE0);
 
         self.frame_id
     }
 
     pub fn bind_texture(&mut self,
@@ -1084,17 +1092,17 @@ impl Device {
         for id in id_list {
             let texture_id = TextureId {
                 name: id,
                 target: target.to_gl_target(),
             };
 
             let texture = Texture {
                 gl: Rc::clone(&self.gl),
-                id: id,
+                id,
                 width: 0,
                 height: 0,
                 format: ImageFormat::Invalid,
                 filter: TextureFilter::Nearest,
                 mode: RenderTargetMode::None,
                 fbo_ids: vec![],
             };
 
@@ -1433,17 +1441,17 @@ impl Device {
         let program = Program {
             gl: Rc::clone(&self.gl),
             name: base_filename.to_owned(),
             id: pid,
             u_transform: -1,
             u_device_pixel_ratio: -1,
             vs_source: get_shader_source(&vs_name, &self.resource_override_path),
             fs_source: get_shader_source(&fs_name, &self.resource_override_path),
-            prefix: prefix,
+            prefix,
             vs_id: None,
             fs_id: None,
         };
 
         let program_id = ProgramId(pid);
 
         debug_assert!(self.programs.contains_key(&program_id) == false);
         self.programs.insert(program_id, program);
@@ -1551,21 +1559,16 @@ impl Device {
             self.gl.uniform_1i(u_layers, TextureSampler::Layers as i32);
         }
 
         let u_tasks = self.gl.get_uniform_location(program.id, "sRenderTasks");
         if u_tasks != -1 {
             self.gl.uniform_1i(u_tasks, TextureSampler::RenderTasks as i32);
         }
 
-        let u_data32 = self.gl.get_uniform_location(program.id, "sData32");
-        if u_data32 != -1 {
-            self.gl.uniform_1i(u_data32, TextureSampler::Data32 as i32);
-        }
-
         let u_resource_cache = self.gl.get_uniform_location(program.id, "sResourceCache");
         if u_resource_cache != -1 {
             self.gl.uniform_1i(u_resource_cache, TextureSampler::ResourceCache as i32);
         }
 
         Ok(())
     }
 
@@ -1623,16 +1626,79 @@ impl Device {
                     device_pixel_ratio: f32) {
         debug_assert!(self.inside_frame);
         self.gl.uniform_matrix_4fv(program.u_transform,
                                false,
                                &transform.to_row_major_array());
         self.gl.uniform_1f(program.u_device_pixel_ratio, device_pixel_ratio);
     }
 
+    pub fn create_pbo(&mut self) -> PBOId {
+        let id = self.gl.gen_buffers(1)[0];
+        PBOId(id)
+    }
+
+    pub fn destroy_pbo(&mut self, id: PBOId) {
+        self.gl.delete_buffers(&[id.0]);
+    }
+
+    pub fn bind_pbo(&mut self, pbo_id: Option<PBOId>) {
+        debug_assert!(self.inside_frame);
+        let pbo_id = pbo_id.unwrap_or(PBOId(0));
+
+        if self.bound_pbo != pbo_id {
+            self.bound_pbo = pbo_id;
+
+            self.gl.bind_buffer(gl::PIXEL_UNPACK_BUFFER, pbo_id.0);
+        }
+    }
+
+    pub fn update_pbo_data<T>(&mut self, data: &[T]) {
+        debug_assert!(self.inside_frame);
+        debug_assert!(self.bound_pbo.0 != 0);
+
+        gl::buffer_data(&*self.gl,
+                        gl::PIXEL_UNPACK_BUFFER,
+                        data,
+                        gl::STREAM_DRAW);
+    }
+
+    pub fn orphan_pbo(&mut self, new_size: usize) {
+        debug_assert!(self.inside_frame);
+        debug_assert!(self.bound_pbo.0 != 0);
+
+        self.gl.buffer_data_untyped(gl::PIXEL_UNPACK_BUFFER,
+                                    new_size as isize,
+                                    ptr::null(),
+                                    gl::STREAM_DRAW);
+    }
+
+    pub fn update_texture_from_pbo(&mut self,
+                                   texture_id: TextureId,
+                                   x0: u32,
+                                   y0: u32,
+                                   width: u32,
+                                   height: u32,
+                                   offset: usize) {
+        debug_assert!(self.inside_frame);
+        debug_assert_eq!(self.textures.get(&texture_id).unwrap().format, ImageFormat::RGBAF32);
+
+        self.bind_texture(DEFAULT_TEXTURE, texture_id);
+
+        self.gl.tex_sub_image_2d_pbo(texture_id.target,
+                                     0,
+                                     x0 as gl::GLint,
+                                     y0 as gl::GLint,
+                                     width as gl::GLint,
+                                     height as gl::GLint,
+                                     gl::RGBA,
+                                     gl::FLOAT,
+                                     offset);
+    }
+
     pub fn update_texture(&mut self,
                           texture_id: TextureId,
                           x0: u32,
                           y0: u32,
                           width: u32,
                           height: u32,
                           stride: Option<u32>,
                           data: &[u8]) {
@@ -1723,23 +1789,23 @@ impl Device {
         self.gl.bind_vertex_array(vao_id);
 
         format.bind(self.gl(), main_vbo_id, instance_vbo_id, vertex_offset, instance_stride);
         ibo_id.bind(self.gl()); // force it to be a part of VAO
 
         let vao = VAO {
             gl: Rc::clone(&self.gl),
             id: vao_id,
-            ibo_id: ibo_id,
-            main_vbo_id: main_vbo_id,
-            instance_vbo_id: instance_vbo_id,
-            instance_stride: instance_stride,
-            owns_indices: owns_indices,
-            owns_vertices: owns_vertices,
-            owns_instances: owns_instances,
+            ibo_id,
+            main_vbo_id,
+            instance_vbo_id,
+            instance_stride,
+            owns_indices,
+            owns_vertices,
+            owns_instances,
         };
 
         self.gl.bind_vertex_array(0);
 
         let vao_id = VAOId(vao_id);
 
         debug_assert!(!self.vaos.contains_key(&vao_id));
         self.vaos.insert(vao_id, vao);
--- a/gfx/webrender/src/ellipse.rs
+++ b/gfx/webrender/src/ellipse.rs
@@ -1,13 +1,13 @@
 /* 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_traits::{LayerPoint, LayerSize};
+use api::{LayerPoint, LayerSize};
 use std::f32::consts::FRAC_PI_2;
 
 /// Number of steps to integrate arc length over.
 const STEP_COUNT: usize = 20;
 
 /// Represents an ellipse centred at a local space origin.
 #[derive(Debug, Clone)]
 pub struct Ellipse {
@@ -18,18 +18,18 @@ pub struct Ellipse {
 impl Ellipse {
     pub fn new(radius: LayerSize) -> Ellipse {
         // Approximate the total length of the first quadrant of this ellipse.
         let total_arc_length = get_simpson_length(FRAC_PI_2,
                                                   radius.width,
                                                   radius.height);
 
         Ellipse {
-            radius: radius,
-            total_arc_length: total_arc_length,
+            radius,
+            total_arc_length,
         }
     }
 
     /// Binary search to estimate the angle of an ellipse
     /// for a given arc length. This only searches over the
     /// first quadrant of an ellipse.
     pub fn find_angle_for_arc_length(&self, arc_length: f32) -> f32 {
         // Clamp arc length to [0, pi].
--- a/gfx/webrender/src/frame.rs
+++ b/gfx/webrender/src/frame.rs
@@ -1,36 +1,36 @@
 /* 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::{BuiltDisplayList, BuiltDisplayListIter, ClipAndScrollInfo, ClipId, ColorF};
+use api::{ComplexClipRegion, DeviceUintRect, DeviceUintSize, DisplayItemRef, Epoch, FilterOp};
+use api::{ImageDisplayItem, ItemRange, LayerPoint, LayerRect, LayerSize, LayerToScrollTransform};
+use api::{LayerVector2D, LayoutSize, LayoutTransform, LocalClip, MixBlendMode, PipelineId};
+use api::{ScrollClamping, ScrollEventPhase, ScrollLayerState, ScrollLocation, ScrollPolicy};
+use api::{SpecificDisplayItem, StackingContext, TileOffset, TransformStyle, WorldPoint};
 use app_units::Au;
+use clip_scroll_tree::{ClipScrollTree, ScrollStates};
 use euclid::rect;
 use fnv::FnvHasher;
 use gpu_cache::GpuCache;
 use internal_types::{ANGLE_FLOAT_TO_FIXED, AxisDirection};
 use internal_types::{LowLevelFilterOp};
 use internal_types::{RendererFrame};
 use frame_builder::{FrameBuilder, FrameBuilderConfig};
-use clip_scroll_tree::{ClipScrollTree, ScrollStates};
+use mask_cache::ClipRegion;
 use profiler::{GpuCacheProfileCounters, TextureCacheProfileCounters};
 use resource_cache::ResourceCache;
 use scene::{Scene, SceneProperties};
 use std::cmp;
 use std::collections::HashMap;
 use std::hash::BuildHasherDefault;
 use tiling::{CompositeOps, DisplayListMap, PrimitiveFlags};
 use util::{ComplexClipRegionHelpers, subtract_rect};
-use webrender_traits::{BuiltDisplayList, BuiltDisplayListIter, ClipAndScrollInfo, ClipDisplayItem};
-use webrender_traits::{ClipId, ClipRegion, ColorF, DeviceUintRect, DeviceUintSize, DisplayItemRef};
-use webrender_traits::{Epoch, FilterOp, ImageDisplayItem, ItemRange, LayerPoint, LayerRect};
-use webrender_traits::{LayerSize, LayerToScrollTransform, LayoutSize, LayoutTransform, LayerVector2D};
-use webrender_traits::{MixBlendMode, PipelineId, ScrollClamping, ScrollEventPhase};
-use webrender_traits::{ScrollLayerState, ScrollLocation, ScrollPolicy, SpecificDisplayItem};
-use webrender_traits::{StackingContext, TileOffset, TransformStyle, WorldPoint};
 
 #[derive(Copy, Clone, PartialEq, PartialOrd, Debug)]
 pub struct FrameId(pub u32);
 
 static DEFAULT_SCROLLBAR_COLOR: ColorF = ColorF { r: 0.3, g: 0.3, b: 0.3, a: 0.6 };
 
 /// Nested display lists cause two types of replacements to ClipIds inside the nesting:
 ///     1. References to the root scroll frame are replaced by the ClipIds that
@@ -92,19 +92,19 @@ struct FlattenContext<'a> {
 }
 
 impl<'a> FlattenContext<'a> {
     fn new(scene: &'a Scene,
            builder: &'a mut FrameBuilder,
            resource_cache: &'a mut ResourceCache)
            -> FlattenContext<'a> {
         FlattenContext {
-            scene: scene,
-            builder: builder,
-            resource_cache: resource_cache,
+            scene,
+            builder,
+            resource_cache,
             replacements: Vec::new(),
             nested_display_list_info: Vec::new(),
             current_nested_display_list_index: 0,
         }
     }
 
     fn push_nested_display_list_ids(&mut self, info: ClipAndScrollInfo) {
         self.current_nested_display_list_index += 1;
@@ -147,16 +147,30 @@ impl<'a> FlattenContext<'a> {
     /// position of a node. We can eventually remove this when clients start handling
     /// reference frames themselves. This method applies these replacements.
     fn apply_scroll_frame_id_replacement(&self, id: ClipId) -> ClipId {
         match self.replacements.last() {
             Some(&(to_replace, replacement)) if to_replace == id => replacement,
             _ => id,
         }
     }
+
+    fn get_complex_clips(&self,
+                         pipeline_id: PipelineId,
+                         complex_clips: ItemRange<ComplexClipRegion>)
+                         -> Vec<ComplexClipRegion> {
+        if complex_clips.is_empty() {
+            return vec![];
+        }
+
+        self.scene.display_lists.get(&pipeline_id)
+                                .expect("No display list?")
+                                .get(complex_clips)
+                                .collect()
+    }
 }
 
 // TODO: doc
 pub struct Frame {
     pub clip_scroll_tree: ClipScrollTree,
     pub pipeline_epoch_map: HashMap<PipelineId, Epoch, BuildHasherDefault<FnvHasher>>,
     id: FrameId,
     frame_builder_config: FrameBuilderConfig,
@@ -222,32 +236,16 @@ impl StackingContextHelpers for Stacking
                     filters.push(LowLevelFilterOp::Sepia(Au::from_f32_px(amount)));
                 }
             }
         }
         filters
     }
 }
 
-fn clip_intersection(original_rect: &LayerRect,
-                     region: &ClipRegion,
-                     display_list: &BuiltDisplayList)
-                     -> Option<LayerRect> {
-    if region.image_mask.is_some() {
-        return None;
-    }
-    let clips = display_list.get(region.complex_clips);
-    let base_rect = region.main.intersection(original_rect);
-    clips.fold(base_rect, |inner_combined, ccr| {
-        inner_combined.and_then(|combined| {
-            ccr.get_inner_rect_full().and_then(|ir| ir.intersection(&combined))
-        })
-    })
-}
-
 impl Frame {
     pub fn new(config: FrameBuilderConfig) -> Frame {
         Frame {
             pipeline_epoch_map: HashMap::default(),
             clip_scroll_tree: ClipScrollTree::new(),
             id: FrameId(0),
             frame_builder: None,
             frame_builder_config: config,
@@ -352,36 +350,49 @@ impl Frame {
 
         self.frame_builder = Some(frame_builder);
         self.clip_scroll_tree.finalize_and_apply_pending_scroll_offsets(old_scrolling_states);
     }
 
     fn flatten_clip<'a>(&mut self,
                         context: &mut FlattenContext,
                         pipeline_id: PipelineId,
-                        parent_id: ClipId,
-                        item: &ClipDisplayItem,
-                        content_rect: &LayerRect,
-                        clip: &ClipRegion) {
-        let clip_viewport = LayerRect::new(content_rect.origin, clip.main.size);
-        let new_clip_id = self.clip_scroll_tree.generate_new_clip_id(pipeline_id);
-        context.builder.add_clip_scroll_node(new_clip_id,
-                                             parent_id,
-                                             pipeline_id,
-                                             &clip_viewport,
-                                             clip,
-                                             &mut self.clip_scroll_tree);
-        let new_id = context.convert_new_id_to_neested(&item.id);
-        context.builder.add_scroll_frame(new_id,
-                                         new_clip_id,
+                        parent_id: &ClipId,
+                        new_clip_id: &ClipId,
+                        clip_region: ClipRegion) {
+        let new_clip_id = context.convert_new_id_to_neested(new_clip_id);
+        context.builder.add_clip_node(new_clip_id,
+                                      *parent_id,
+                                      pipeline_id,
+                                      clip_region,
+                                      &mut self.clip_scroll_tree);
+    }
+
+    fn flatten_scroll_frame<'a>(&mut self,
+                                context: &mut FlattenContext,
+                                pipeline_id: PipelineId,
+                                parent_id: &ClipId,
+                                new_scroll_frame_id: &ClipId,
+                                frame_rect: &LayerRect,
+                                content_rect: &LayerRect,
+                                clip_region: ClipRegion) {
+        let clip_id = self.clip_scroll_tree.generate_new_clip_id(pipeline_id);
+        context.builder.add_clip_node(clip_id,
+                                      *parent_id,
+                                      pipeline_id,
+                                      clip_region,
+                                      &mut self.clip_scroll_tree);
+
+        let new_scroll_frame_id = context.convert_new_id_to_neested(new_scroll_frame_id);
+        context.builder.add_scroll_frame(new_scroll_frame_id,
+                                         clip_id,
                                          pipeline_id,
-                                         &content_rect,
-                                         &clip_viewport,
+                                         &frame_rect,
+                                         &content_rect.size,
                                          &mut self.clip_scroll_tree);
-
     }
 
     fn flatten_stacking_context<'a>(&mut self,
                                     traversal: &mut BuiltDisplayListIter<'a>,
                                     pipeline_id: PipelineId,
                                     context: &mut FlattenContext,
                                     context_scroll_node_id: ClipId,
                                     mut reference_frame_relative_offset: LayerVector2D,
@@ -444,17 +455,16 @@ impl Frame {
             reference_frame_relative_offset = LayerVector2D::new(
                 reference_frame_relative_offset.x + bounds.origin.x,
                 reference_frame_relative_offset.y + bounds.origin.y);
         }
 
         context.builder.push_stacking_context(&reference_frame_relative_offset,
                                               pipeline_id,
                                               composition_operations,
-                                              *bounds,
                                               stacking_context.transform_style);
 
         self.flatten_items(traversal,
                            pipeline_id,
                            context,
                            reference_frame_relative_offset);
 
         if stacking_context.scroll_policy == ScrollPolicy::Fixed {
@@ -468,17 +478,16 @@ impl Frame {
 
         context.builder.pop_stacking_context();
     }
 
     fn flatten_iframe<'a>(&mut self,
                           pipeline_id: PipelineId,
                           parent_id: ClipId,
                           bounds: &LayerRect,
-                          clip_region: &ClipRegion,
                           context: &mut FlattenContext,
                           reference_frame_relative_offset: LayerVector2D) {
         let pipeline = match context.scene.pipeline_map.get(&pipeline_id) {
             Some(pipeline) => pipeline,
             None => return,
         };
 
         let display_list = match context.scene.display_lists.get(&pipeline_id) {
@@ -489,37 +498,29 @@ impl Frame {
         self.pipeline_epoch_map.insert(pipeline_id, pipeline.epoch);
 
         let iframe_rect = LayerRect::new(LayerPoint::zero(), bounds.size);
         let transform = LayerToScrollTransform::create_translation(
             reference_frame_relative_offset.x + bounds.origin.x,
             reference_frame_relative_offset.y + bounds.origin.y,
             0.0);
 
-        let new_clip_id = self.clip_scroll_tree.generate_new_clip_id(pipeline_id);
-        context.builder.add_clip_scroll_node(new_clip_id,
-                                             parent_id,
-                                             parent_id.pipeline_id(),
-                                             bounds,
-                                             clip_region,
-                                             &mut self.clip_scroll_tree);
-
         let iframe_reference_frame_id =
-            context.builder.push_reference_frame(Some(new_clip_id),
+            context.builder.push_reference_frame(Some(parent_id),
                                                  pipeline_id,
                                                  &iframe_rect,
                                                  &transform,
                                                  &mut self.clip_scroll_tree);
 
         context.builder.add_scroll_frame(
             ClipId::root_scroll_node(pipeline_id),
             iframe_reference_frame_id,
             pipeline_id,
-            &LayerRect::new(LayerPoint::zero(), pipeline.content_size),
             &iframe_rect,
+            &pipeline.content_size,
             &mut self.clip_scroll_tree);
 
         self.flatten_root(&mut display_list.iter(), pipeline_id, context, &pipeline.content_size);
 
         context.builder.pop_reference_frame();
     }
 
     fn flatten_item<'a, 'b>(&mut self,
@@ -534,136 +535,121 @@ impl Frame {
         let unreplaced_scroll_id = clip_and_scroll.scroll_node_id;
         clip_and_scroll.scroll_node_id =
             context.apply_scroll_frame_id_replacement(clip_and_scroll.scroll_node_id);
 
         match *item.item() {
             SpecificDisplayItem::WebGL(ref info) => {
                 context.builder.add_webgl_rectangle(clip_and_scroll,
                                                     item.rect(),
-                                                    item.clip_region(),
+                                                    item.local_clip(),
                                                     info.context_id);
             }
             SpecificDisplayItem::Image(ref info) => {
                 let image = context.resource_cache.get_image_properties(info.image_key);
                 if let Some(tile_size) = image.tiling {
                     // The image resource is tiled. We have to generate an image primitive
                     // for each tile.
                     let image_size = DeviceUintSize::new(image.descriptor.width,
                                                          image.descriptor.height);
                     self.decompose_image(clip_and_scroll,
                                          context,
                                          &item.rect(),
-                                         item.clip_region(),
+                                         item.local_clip(),
                                          info,
                                          image_size,
                                          tile_size as u32);
                 } else {
                     context.builder.add_image(clip_and_scroll,
                                               item.rect(),
-                                              item.clip_region(),
+                                              item.local_clip(),
                                               &info.stretch_size,
                                               &info.tile_spacing,
                                               None,
                                               info.image_key,
                                               info.image_rendering,
                                               None);
                 }
             }
             SpecificDisplayItem::YuvImage(ref info) => {
                 context.builder.add_yuv_image(clip_and_scroll,
                                               item.rect(),
-                                              item.clip_region(),
+                                              item.local_clip(),
                                               info.yuv_data,
                                               info.color_space,
                                               info.image_rendering);
             }
             SpecificDisplayItem::Text(ref text_info) => {
                 context.builder.add_text(clip_and_scroll,
                                          item.rect(),
-                                         item.clip_region(),
+                                         item.local_clip(),
                                          text_info.font_key,
                                          text_info.size,
                                          text_info.blur_radius,
                                          &text_info.color,
                                          item.glyphs(),
                                          item.display_list().get(item.glyphs()).count(),
                                          text_info.glyph_options);
             }
             SpecificDisplayItem::Rectangle(ref info) => {
-                // Try to extract the opaque inner rectangle out of the clipped primitive.
-                if let Some(opaque_rect) = clip_intersection(&item.rect(),
-                                                             item.clip_region(),
-                                                             item.display_list()) {
-                    let mut results = Vec::new();
-                    subtract_rect(&item.rect(), &opaque_rect, &mut results);
-                    // The inner rectangle is considered opaque within this layer.
-                    // It may still inherit some masking from the clip stack.
+                if !self.try_to_add_rectangle_splitting_on_clip(context,
+                                                                &item.rect(),
+                                                                item.local_clip(),
+                                                                &info.color,
+                                                                &clip_and_scroll) {
                     context.builder.add_solid_rectangle(clip_and_scroll,
-                                                        &opaque_rect,
-                                                        &ClipRegion::simple(&item.clip_region().main),
+                                                        &item.rect(),
+                                                        item.local_clip(),
                                                         &info.color,
                                                         PrimitiveFlags::None);
-                    for transparent_rect in &results {
-                        context.builder.add_solid_rectangle(clip_and_scroll,
-                                                            transparent_rect,
-                                                            item.clip_region(),
-                                                            &info.color,
-                                                            PrimitiveFlags::None);
-                    }
-                } else {
-                    context.builder.add_solid_rectangle(clip_and_scroll,
-                                                        &item.rect(),
-                                                        item.clip_region(),
-                                                        &info.color,
-                                                        PrimitiveFlags::None);
+
                 }
             }
             SpecificDisplayItem::Gradient(ref info) => {
                 context.builder.add_gradient(clip_and_scroll,
                                              item.rect(),
-                                             item.clip_region(),
+                                             item.local_clip(),
                                              info.gradient.start_point,
                                              info.gradient.end_point,
                                              item.gradient_stops(),
                                              item.display_list()
                                                  .get(item.gradient_stops()).count(),
                                              info.gradient.extend_mode,
                                              info.tile_size,
                                              info.tile_spacing);
             }
             SpecificDisplayItem::RadialGradient(ref info) => {
                 context.builder.add_radial_gradient(clip_and_scroll,
                                                     item.rect(),
-                                                    item.clip_region(),
+                                                    item.local_clip(),
                                                     info.gradient.start_center,
                                                     info.gradient.start_radius,
                                                     info.gradient.end_center,
                                                     info.gradient.end_radius,
                                                     info.gradient.ratio_xy,
                                                     item.gradient_stops(),
                                                     info.gradient.extend_mode,
                                                     info.tile_size,
                                                     info.tile_spacing);
             }
             SpecificDisplayItem::BoxShadow(ref box_shadow_info) => {
                 context.builder.add_box_shadow(clip_and_scroll,
                                                &box_shadow_info.box_bounds,
-                                               item.clip_region(),
+                                               item.local_clip(),
                                                &box_shadow_info.offset,
                                                &box_shadow_info.color,
                                                box_shadow_info.blur_radius,
                                                box_shadow_info.spread_radius,
                                                box_shadow_info.border_radius,
                                                box_shadow_info.clip_mode);
             }
             SpecificDisplayItem::Border(ref info) => {
                 context.builder.add_border(clip_and_scroll,
                                            item.rect(),
-                                           item.clip_region(),
+                                           item.local_clip(),
                                            info,
                                            item.gradient_stops(),
                                            item.display_list()
                                                .get(item.gradient_stops()).count());
             }
             SpecificDisplayItem::PushStackingContext(ref info) => {
                 let mut subtraversal = item.sub_iter();
                 self.flatten_stacking_context(&mut subtraversal,
@@ -675,89 +661,162 @@ impl Frame {
                                               &info.stacking_context,
                                               item.filters());
                 return Some(subtraversal);
             }
             SpecificDisplayItem::Iframe(ref info) => {
                 self.flatten_iframe(info.pipeline_id,
                                     clip_and_scroll.scroll_node_id,
                                     &item.rect(),
-                                    &item.clip_region(),
                                     context,
                                     reference_frame_relative_offset);
             }
             SpecificDisplayItem::Clip(ref info) => {
-                let content_rect = &item.rect().translate(&reference_frame_relative_offset);
+                let complex_clips = context.get_complex_clips(pipeline_id, item.complex_clip().0);
+                let mut clip_region = ClipRegion::for_clip_node(*item.local_clip().clip_rect(),
+                                                                complex_clips,
+                                                                info.image_mask);
+                clip_region.origin += reference_frame_relative_offset;
+
                 self.flatten_clip(context,
                                   pipeline_id,
-                                  clip_and_scroll.scroll_node_id,
-                                  &info,
-                                  &content_rect,
-                                  item.clip_region());
+                                  &clip_and_scroll.scroll_node_id,
+                                  &info.id,
+                                  clip_region);
+            }
+            SpecificDisplayItem::ScrollFrame(ref info) => {
+                let complex_clips = context.get_complex_clips(pipeline_id, item.complex_clip().0);
+                let mut clip_region = ClipRegion::for_clip_node(*item.local_clip().clip_rect(),
+                                                                complex_clips,
+                                                                info.image_mask);
+                clip_region.origin += reference_frame_relative_offset;
+
+                // Just use clip rectangle as the frame rect for this scroll frame.
+                // This is only interesting when calculating scroll extents for the
+                // ClipScrollNode::scroll(..) API
+                let frame_rect = item.local_clip()
+                                     .clip_rect()
+                                     .translate(&reference_frame_relative_offset);
+                let content_rect = item.rect().translate(&reference_frame_relative_offset);
+                self.flatten_scroll_frame(context,
+                                          pipeline_id,
+                                          &clip_and_scroll.scroll_node_id,
+                                          &info.id,
+                                          &frame_rect,
+                                          &content_rect,
+                                          clip_region);
             }
             SpecificDisplayItem::PushNestedDisplayList => {
                 // Using the clip and scroll already processed for nesting here
                 // means that in the case of multiple nested display lists, we
                 // will enter the outermost ids into the table and avoid having
                 // to do a replacement for every level of nesting.
                 context.push_nested_display_list_ids(clip_and_scroll);
             }
             SpecificDisplayItem::PopNestedDisplayList => context.pop_nested_display_list_ids(),
 
             // Do nothing; these are dummy items for the display list parser
-            SpecificDisplayItem::SetGradientStops | SpecificDisplayItem::SetClipRegion(_) => { }
+            SpecificDisplayItem::SetGradientStops => { }
 
             SpecificDisplayItem::PopStackingContext =>
                 unreachable!("Should have returned in parent method."),
         }
         None
     }
 
+    /// Try to optimize the rendering of a solid rectangle that is clipped by a single
+    /// rounded rectangle, by only masking the parts of the rectangle that intersect
+    /// the rounded parts of the clip. This is pretty simple now, so has a lot of
+    /// potential for further optimizations.
+    fn try_to_add_rectangle_splitting_on_clip(&mut self,
+                                              context: &mut FlattenContext,
+                                              rect: &LayerRect,
+                                              local_clip: &LocalClip,
+                                              color: &ColorF,
+                                              clip_and_scroll: &ClipAndScrollInfo)
+                                              -> bool {
+        // If this rectangle is not opaque, splitting the rectangle up
+        // into an inner opaque region just ends up hurting batching and
+        // doing more work than necessary.
+        if color.a != 1.0 {
+            return false;
+        }
+
+        let inner_unclipped_rect = match local_clip {
+            &LocalClip::Rect(_) => return false,
+            &LocalClip::RoundedRect(_, ref region) => region.get_inner_rect_full(),
+        };
+        let inner_unclipped_rect = match inner_unclipped_rect {
+            Some(rect) => rect,
+            None => return false,
+        };
+
+        // The inner rectangle is not clipped by its assigned clipping node, so we can
+        // let it be clipped by the parent of the clipping node, which may result in
+        // less masking some cases.
+        let mut clipped_rects = Vec::new();
+        subtract_rect(rect, &inner_unclipped_rect, &mut clipped_rects);
+
+        context.builder.add_solid_rectangle(*clip_and_scroll,
+                                            &inner_unclipped_rect,
+                                            &LocalClip::from(*local_clip.clip_rect()),
+                                            color,
+                                            PrimitiveFlags::None);
+
+        for clipped_rect in &clipped_rects {
+            context.builder.add_solid_rectangle(*clip_and_scroll,
+                                                clipped_rect,
+                                                local_clip,
+                                                color,
+                                                PrimitiveFlags::None);
+        }
+        true
+    }
+
     fn flatten_root<'a>(&mut self,
                         traversal: &mut BuiltDisplayListIter<'a>,
                         pipeline_id: PipelineId,
                         context: &mut FlattenContext,
                         content_size: &LayoutSize) {
-        let root_bounds = LayerRect::new(LayerPoint::zero(), *content_size);
         context.builder.push_stacking_context(&LayerVector2D::zero(),
                                               pipeline_id,
                                               CompositeOps::default(),
-                                              root_bounds,
                                               TransformStyle::Flat);
 
         // We do this here, rather than above because we want any of the top-level
         // stacking contexts in the display list to be treated like root stacking contexts.
         // FIXME(mrobinson): Currently only the first one will, which for the moment is
         // sufficient for all our use cases.
         context.builder.notify_waiting_for_root_stacking_context();
 
         // For the root pipeline, there's no need to add a full screen rectangle
         // here, as it's handled by the framebuffer clear.
         let clip_id = ClipId::root_scroll_node(pipeline_id);
         if context.scene.root_pipeline_id != Some(pipeline_id) {
             if let Some(pipeline) = context.scene.pipeline_map.get(&pipeline_id) {
                 if let Some(bg_color) = pipeline.background_color {
+                    let root_bounds = LayerRect::new(LayerPoint::zero(), *content_size);
                     context.builder.add_solid_rectangle(ClipAndScrollInfo::simple(clip_id),
                                                         &root_bounds,
-                                                        &ClipRegion::simple(&root_bounds),
+                                                        &LocalClip::from(root_bounds),
                                                         &bg_color,
                                                         PrimitiveFlags::None);
                 }
             }
         }
 
 
         self.flatten_items(traversal, pipeline_id, context, LayerVector2D::zero());
 
         if self.frame_builder_config.enable_scrollbars {
             let scrollbar_rect = LayerRect::new(LayerPoint::zero(), LayerSize::new(10.0, 70.0));
             context.builder.add_solid_rectangle(
                 ClipAndScrollInfo::simple(clip_id),
                 &scrollbar_rect,
-                &ClipRegion::simple(&scrollbar_rect),
+                &LocalClip::from(scrollbar_rect),
                 &DEFAULT_SCROLLBAR_COLOR,
                 PrimitiveFlags::Scrollbar(self.clip_scroll_tree.topmost_scrolling_node_id(), 4.0));
         }
 
         context.builder.pop_stacking_context();
     }
 
     fn flatten_items<'a>(&mut self,
@@ -797,27 +856,27 @@ impl Frame {
     /// an image where the width is too bug but the height is not).
     ///
     /// decompose_image and decompose_image_row handle image repetitions while decompose_tiled_image
     /// takes care of the decomposition required by the internal tiling of the image.
     fn decompose_image(&mut self,
                        clip_and_scroll: ClipAndScrollInfo,
                        context: &mut FlattenContext,
                        item_rect: &LayerRect,
-                       item_clip: &ClipRegion,
+                       item_local_clip: &LocalClip,
                        info: &ImageDisplayItem,
                        image_size: DeviceUintSize,
                        tile_size: u32) {
         let no_vertical_tiling = image_size.height <= tile_size;
         let no_vertical_spacing = info.tile_spacing.height == 0.0;
         if no_vertical_tiling && no_vertical_spacing {
             self.decompose_image_row(clip_and_scroll,
                                      context,
                                      item_rect,
-                                     item_clip,
+                                     item_local_clip,
                                      info,
                                      image_size,
                                      tile_size);
             return;
         }
 
         // Decompose each vertical repetition into rows.
         let layout_stride = info.stretch_size.height + info.tile_spacing.height;
@@ -827,39 +886,39 @@ impl Frame {
                 item_rect.origin.x,
                 item_rect.origin.y + (i as f32) * layout_stride,
                 item_rect.size.width,
                 info.stretch_size.height
             ).intersection(item_rect) {
                 self.decompose_image_row(clip_and_scroll,
                                          context,
                                          &row_rect,
-                                         item_clip,
+                                         item_local_clip,
                                          info,
                                          image_size,
                                          tile_size);
             }
         }
     }
 
     fn decompose_image_row(&mut self,
                            clip_and_scroll: ClipAndScrollInfo,
                            context: &mut FlattenContext,
                            item_rect: &LayerRect,
-                           item_clip: &ClipRegion,
+                           item_local_clip: &LocalClip,
                            info: &ImageDisplayItem,
                            image_size: DeviceUintSize,
                            tile_size: u32) {
         let no_horizontal_tiling = image_size.width <= tile_size;
         let no_horizontal_spacing = info.tile_spacing.width == 0.0;
         if no_horizontal_tiling && no_horizontal_spacing {
             self.decompose_tiled_image(clip_and_scroll,
                                        context,
                                        item_rect,
-                                       item_clip,
+                                       item_local_clip,
                                        info,
                                        image_size,
                                        tile_size);
             return;
         }
 
         // Decompose each horizontal repetition.
         let layout_stride = info.stretch_size.width + info.tile_spacing.width;
@@ -869,29 +928,29 @@ impl Frame {
                 item_rect.origin.x + (i as f32) * layout_stride,
                 item_rect.origin.y,
                 info.stretch_size.width,
                 item_rect.size.height,
             ).intersection(item_rect) {
                 self.decompose_tiled_image(clip_and_scroll,
                                            context,
                                            &decomposed_rect,
-                                           item_clip,
+                                           item_local_clip,
                                            info,
                                            image_size,
                                            tile_size);
             }
         }
     }
 
     fn decompose_tiled_image(&mut self,
                              clip_and_scroll: ClipAndScrollInfo,
                              context: &mut FlattenContext,
                              item_rect: &LayerRect,
-                             item_clip: &ClipRegion,
+                             item_local_clip: &LocalClip,
                              info: &ImageDisplayItem,
                              image_size: DeviceUintSize,
                              tile_size: u32) {
         // The image resource is tiled. We have to generate an image primitive
         // for each tile.
         // We need to do this because the image is broken up into smaller tiles in the texture
         // cache and the image shader is not able to work with this type of sparse representation.
 
@@ -960,76 +1019,76 @@ impl Frame {
         // Zero means the image size is a multiple of the tile size.
         let leftover = DeviceUintSize::new(image_size.width % tile_size, image_size.height % tile_size);
 
         for ty in 0..num_tiles_y {
             for tx in 0..num_tiles_x {
                 self.add_tile_primitive(clip_and_scroll,
                                         context,
                                         item_rect,
-                                        item_clip,
+                                        item_local_clip,
                                         info,
                                         TileOffset::new(tx, ty),
                                         stretched_tile_size,
                                         1.0, 1.0,
                                         repeat_x, repeat_y);
             }
             if leftover.width != 0 {
                 // Tiles on the right edge that are smaller than the tile size.
                 self.add_tile_primitive(clip_and_scroll,
                                         context,
                                         item_rect,
-                                        item_clip,
+                                        item_local_clip,
                                         info,
                                         TileOffset::new(num_tiles_x, ty),
                                         stretched_tile_size,
                                         (leftover.width as f32) / tile_size_f32,
                                         1.0,
                                         repeat_x, repeat_y);
             }
         }
 
         if leftover.height != 0 {
             for tx in 0..num_tiles_x {
                 // Tiles on the bottom edge that are smaller than the tile size.
                 self.add_tile_primitive(clip_and_scroll,
                                         context,
                                         item_rect,
-                                        item_clip,
+                                        item_local_clip,
                                         info,
                                         TileOffset::new(tx, num_tiles_y),
                                         stretched_tile_size,
                                         1.0,
                                         (leftover.height as f32) / tile_size_f32,
                                         repeat_x,
                                         repeat_y);
             }
 
             if leftover.width != 0 {
                 // Finally, the bottom-right tile with a "leftover" size.
                 self.add_tile_primitive(clip_and_scroll,
                                         context,
                                         item_rect,
-                                        item_clip,
+                                        item_local_clip,
                                         info,
                                         TileOffset::new(num_tiles_x, num_tiles_y),
                                         stretched_tile_size,
                                         (leftover.width as f32) / tile_size_f32,
                                         (leftover.height as f32) / tile_size_f32,
                                         repeat_x,
                                         repeat_y);
             }
         }
     }
 
     fn add_tile_primitive(&mut self,
                           clip_and_scroll: ClipAndScrollInfo,
                           context: &mut FlattenContext,
                           item_rect: &LayerRect,
-                          item_clip: &ClipRegion,
+                          item_local_clip: &LocalClip,
                           info: &ImageDisplayItem,
                           tile_offset: TileOffset,
                           stretched_tile_size: LayerSize,
                           tile_ratio_width: f32,
                           tile_ratio_height: f32,
                           repeat_x: bool,
                           repeat_y: bool) {
         // If the the image is tiled along a given axis, we can't have the shader compute
@@ -1063,17 +1122,17 @@ impl Frame {
             assert_eq!(tile_offset.y, 0);
             prim_rect.size.height = item_rect.size.height;
         }
 
         // Fix up the primitive's rect if it overflows the original item rect.
         if let Some(prim_rect) = prim_rect.intersection(item_rect) {
             context.builder.add_image(clip_and_scroll,
                                       prim_rect,
-                                      item_clip,
+                                      item_local_clip,
                                       &stretched_size,
                                       &info.tile_spacing,
                                       None,
                                       info.image_key,
                                       info.image_rendering,
                                       Some(tile_offset));
         }
     }
--- a/gfx/webrender/src/frame_builder.rs
+++ b/gfx/webrender/src/frame_builder.rs
@@ -1,46 +1,44 @@
 /* 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::{BorderDetails, BorderDisplayItem, BoxShadowClipMode, ClipAndScrollInfo, ClipId, ColorF};
+use api::{DeviceIntPoint, DeviceIntRect, DeviceIntSize, DeviceUintRect, DeviceUintSize};
+use api::{ExtendMode, FontKey, FontRenderMode, GlyphInstance, GlyphOptions, GradientStop};
+use api::{ImageKey, ImageRendering, ItemRange, LayerPoint, LayerRect, LayerSize};
+use api::{LayerToScrollTransform, LayerVector2D, LocalClip, PipelineId, RepeatMode, TileOffset};
+use api::{TransformStyle, WebGLContextId, WorldPixel, YuvColorSpace, YuvData};
 use app_units::Au;
 use frame::FrameId;
 use gpu_cache::GpuCache;
-use gpu_store::GpuStoreAddress;
 use internal_types::HardwareCompositeOp;
-use mask_cache::{ClipMode, ClipSource, MaskCacheInfo, RegionMode};
+use mask_cache::{ClipMode, ClipRegion, ClipSource, MaskCacheInfo};
 use plane_split::{BspSplitter, Polygon, Splitter};
 use prim_store::{GradientPrimitiveCpu, ImagePrimitiveCpu};
 use prim_store::{ImagePrimitiveKind, PrimitiveContainer, PrimitiveIndex};
 use prim_store::{PrimitiveStore, RadialGradientPrimitiveCpu};
 use prim_store::{RectanglePrimitive, TextRunPrimitiveCpu};
 use prim_store::{BoxShadowPrimitiveCpu, TexelRect, YuvImagePrimitiveCpu};
 use profiler::{FrameProfileCounters, GpuCacheProfileCounters, TextureCacheProfileCounters};
-use render_task::{AlphaRenderItem, MaskCacheKey, MaskResult, RenderTask, RenderTaskIndex};
+use render_task::{AlphaRenderItem, ClipWorkItem, MaskCacheKey, RenderTask, RenderTaskIndex};
 use render_task::{RenderTaskId, RenderTaskLocation};
 use resource_cache::ResourceCache;
 use clip_scroll_node::{ClipInfo, ClipScrollNode, NodeType};
 use clip_scroll_tree::ClipScrollTree;
 use std::{cmp, f32, i32, mem, usize};
 use std::collections::HashMap;
 use euclid::{SideOffsets2D, vec2, vec3};
 use tiling::{ContextIsolation, StackingContextIndex};
 use tiling::{ClipScrollGroup, ClipScrollGroupIndex, CompositeOps, DisplayListMap, Frame};
 use tiling::{PackedLayer, PackedLayerIndex, PrimitiveFlags, PrimitiveRunCmd, RenderPass};
 use tiling::{RenderTargetContext, RenderTaskCollection, ScrollbarPrimitive, StackingContext};
 use util::{self, pack_as_float, subtract_rect, recycle_vec};
-use util::RectHelpers;
-use webrender_traits::{BorderDetails, BorderDisplayItem, BoxShadowClipMode, ClipAndScrollInfo};
-use webrender_traits::{ClipId, ClipRegion, ColorF, DeviceIntPoint, DeviceIntRect, DeviceIntSize};
-use webrender_traits::{DeviceUintRect, DeviceUintSize, ExtendMode, FontKey, FontRenderMode};
-use webrender_traits::{GlyphInstance, GlyphOptions, GradientStop, ImageKey, ImageRendering};
-use webrender_traits::{ItemRange, LayerPoint, LayerRect, LayerSize, LayerToScrollTransform};
-use webrender_traits::{PipelineId, RepeatMode, TileOffset, TransformStyle, WebGLContextId};
-use webrender_traits::{WorldPixel, YuvColorSpace, YuvData, LayerVector2D};
+use util::{MatrixHelpers, RectHelpers};
 
 #[derive(Debug, Clone)]
 struct ImageBorderSegment {
     geom_rect: LayerRect,
     sub_rect: TexelRect,
     stretch_size: LayerSize,
     tile_spacing: LayerSize,
 }
@@ -57,51 +55,51 @@ impl ImageBorderSegment {
 
         let image_size = LayerSize::new(sub_rect.uv1.x - sub_rect.uv0.x,
                                         sub_rect.uv1.y - sub_rect.uv0.y);
 
         let stretch_size_x = match repeat_horizontal {
             RepeatMode::Stretch => rect.size.width,
             RepeatMode::Repeat => image_size.width,
             RepeatMode::Round | RepeatMode::Space => {
-                println!("Round/Space not supported yet!");
+                error!("Round/Space not supported yet!");
                 rect.size.width
             }
         };
 
         let stretch_size_y = match repeat_vertical {
             RepeatMode::Stretch => rect.size.height,
             RepeatMode::Repeat => image_size.height,
             RepeatMode::Round | RepeatMode::Space => {
-                println!("Round/Space not supported yet!");
+                error!("Round/Space not supported yet!");
                 rect.size.height
             }
         };
 
         ImageBorderSegment {
             geom_rect: rect,
-            sub_rect: sub_rect,
+            sub_rect,
             stretch_size: LayerSize::new(stretch_size_x, stretch_size_y),
-            tile_spacing: tile_spacing,
+            tile_spacing,
         }
     }
 }
 
 /// Construct a polygon from stacking context boundaries.
 /// `anchor` here is an index that's going to be preserved in all the
 /// splits of the polygon.
-fn make_polygon(sc: &StackingContext, node: &ClipScrollNode, anchor: usize)
-                -> Polygon<f32, WorldPixel> {
-    //TODO: only work with `sc.local_bounds` worth of space
-    // This can be achieved by moving the `sc.local_bounds.origin` shift
+fn make_polygon(stacking_context: &StackingContext, node: &ClipScrollNode,
+                anchor: usize) -> Polygon<f32, WorldPixel> {
+    //TODO: only work with `isolated_items_bounds.size` worth of space
+    // This can be achieved by moving the `origin` shift
     // from the primitive local coordinates into the layer transformation.
     // Which in turn needs it to be a render task property obeyed by all primitives
     // upon rendering, possibly not limited to `write_*_vertex` implementations.
-    let size = sc.local_bounds.bottom_right();
-    let bounds = LayerRect::new(sc.reference_frame_offset.to_point(), LayerSize::new(size.x, size.y));
+    let size = stacking_context.isolated_items_bounds.bottom_right();
+    let bounds = LayerRect::new(LayerPoint::zero(), LayerSize::new(size.x, size.y));
     Polygon::from_transformed_rect(bounds, node.world_content_transform, anchor)
 }
 
 #[derive(Clone, Copy)]
 pub struct FrameBuilderConfig {
     pub enable_scrollbars: bool,
     pub default_font_render_mode: FontRenderMode,
     pub debug: bool,
@@ -145,35 +143,35 @@ impl FrameBuilder {
                     stacking_context_store: recycle_vec(prev.stacking_context_store),
                     clip_scroll_group_store: recycle_vec(prev.clip_scroll_group_store),
                     cmds: recycle_vec(prev.cmds),
                     packed_layers: recycle_vec(prev.packed_layers),
                     scrollbar_prims: recycle_vec(prev.scrollbar_prims),
                     reference_frame_stack: recycle_vec(prev.reference_frame_stack),
                     stacking_context_stack: recycle_vec(prev.stacking_context_stack),
                     prim_store: prev.prim_store.recycle(),
-                    screen_size: screen_size,
-                    background_color: background_color,
-                    config: config,
+                    screen_size,
+                    background_color,
+                    config,
                     has_root_stacking_context: false,
                 }
             }
             None => {
                 FrameBuilder {
                     stacking_context_store: Vec::new(),
                     clip_scroll_group_store: Vec::new(),
                     cmds: Vec::new(),
                     packed_layers: Vec::new(),
                     scrollbar_prims: Vec::new(),
                     reference_frame_stack: Vec::new(),
                     stacking_context_stack: Vec::new(),
                     prim_store: PrimitiveStore::new(),
-                    screen_size: screen_size,
-                    background_color: background_color,
-                    config: config,
+                    screen_size,
+                    background_color,
+                    config,
                     has_root_stacking_context: false,
                 }
             }
         }
     }
 
     pub fn create_clip_scroll_group_if_necessary(&mut self,
                                                  stacking_context_index: StackingContextIndex,
@@ -185,36 +183,37 @@ impl FrameBuilder {
         let group_index = self.create_clip_scroll_group(stacking_context_index, info);
         let stacking_context = &mut self.stacking_context_store[stacking_context_index.0];
         stacking_context.clip_scroll_groups.push(group_index);
     }
 
     pub fn add_primitive(&mut self,
                          clip_and_scroll: ClipAndScrollInfo,
                          rect: &LayerRect,
-                         clip_region: &ClipRegion,
+                         local_clip: &LocalClip,
                          extra_clips: &[ClipSource],
                          container: PrimitiveContainer)
                          -> PrimitiveIndex {
         let stacking_context_index = *self.stacking_context_stack.last().unwrap();
 
         self.create_clip_scroll_group_if_necessary(stacking_context_index, clip_and_scroll);
 
-        let mut clip_sources = Vec::new();
-        if clip_region.is_complex() {
-            clip_sources.push(ClipSource::Region(clip_region.clone(), RegionMode::ExcludeRect));
+        let mut clip_sources = extra_clips.to_vec();
+        if let &LocalClip::RoundedRect(_, _) = local_clip {
+            clip_sources.push(ClipSource::Region(ClipRegion::for_local_clip(local_clip)))
         }
 
-        clip_sources.extend(extra_clips.iter().cloned());
-
-        let clip_info = MaskCacheInfo::new(&clip_sources,
-                                           &mut self.prim_store.gpu_data32);
+        let clip_info = if !clip_sources.is_empty() {
+            Some(MaskCacheInfo::new(&clip_sources))
+        } else {
+            None
+        };
 
         let prim_index = self.prim_store.add_primitive(rect,
-                                                       &clip_region.main,
+                                                       &local_clip.clip_rect(),
                                                        clip_sources,
                                                        clip_info,
                                                        container);
 
         match self.cmds.last_mut().unwrap() {
             &mut PrimitiveRunCmd::PrimitiveRun(_run_prim_index, ref mut count, run_clip_and_scroll)
                 if run_clip_and_scroll == clip_and_scroll => {
                     debug_assert!(_run_prim_index.0 + *count == prim_index.0);
@@ -233,35 +232,34 @@ impl FrameBuilder {
     pub fn create_clip_scroll_group(&mut self,
                                     stacking_context_index: StackingContextIndex,
                                     info: ClipAndScrollInfo)
                                     -> ClipScrollGroupIndex {
         let packed_layer_index = PackedLayerIndex(self.packed_layers.len());
         self.packed_layers.push(PackedLayer::empty());
 
         self.clip_scroll_group_store.push(ClipScrollGroup {
-            stacking_context_index: stacking_context_index,
+            stacking_context_index,
             scroll_node_id: info.scroll_node_id,
             clip_node_id: info.clip_node_id(),
-            packed_layer_index: packed_layer_index,
+            packed_layer_index,
             screen_bounding_rect: None,
          });
 
         ClipScrollGroupIndex(self.clip_scroll_group_store.len() - 1, info)
     }
 
     pub fn notify_waiting_for_root_stacking_context(&mut self) {
         self.has_root_stacking_context = false;
     }
 
     pub fn push_stacking_context(&mut self,
                                  reference_frame_offset: &LayerVector2D,
                                  pipeline_id: PipelineId,
                                  composite_ops: CompositeOps,
-                                 local_bounds: LayerRect,
                                  transform_style: TransformStyle) {
         if let Some(parent_index) = self.stacking_context_stack.last() {
             let parent_is_root = self.stacking_context_store[parent_index.0].is_page_root;
 
             if composite_ops.mix_blend_mode.is_some() && !parent_is_root {
                 // the parent stacking context of a stacking context with mix-blend-mode
                 // must be drawn with a transparent background, unless the parent stacking context
                 // is the root of the page
@@ -274,17 +272,16 @@ impl FrameBuilder {
         }
 
         let stacking_context_index = StackingContextIndex(self.stacking_context_store.len());
         let reference_frame_id = self.current_reference_frame_id();
         self.stacking_context_store.push(StackingContext::new(pipeline_id,
                                                               *reference_frame_offset,
                                                               !self.has_root_stacking_context,
                                                               reference_frame_id,
-                                                              local_bounds,
                                                               transform_style,
                                                               composite_ops));
         self.has_root_stacking_context = true;
         self.cmds.push(PrimitiveRunCmd::PushStackingContext(stacking_context_index));
         self.stacking_context_stack.push(stacking_context_index);
     }
 
     pub fn pop_stacking_context(&mut self) {
@@ -352,98 +349,90 @@ impl FrameBuilder {
         self.push_reference_frame(None, pipeline_id, &viewport_rect, identity, clip_scroll_tree);
 
         let topmost_scrolling_node_id = ClipId::root_scroll_node(pipeline_id);
         clip_scroll_tree.topmost_scrolling_node_id = topmost_scrolling_node_id;
 
         self.add_scroll_frame(topmost_scrolling_node_id,
                               clip_scroll_tree.root_reference_frame_id,
                               pipeline_id,
-                              &LayerRect::new(LayerPoint::zero(), *content_size),
                               &viewport_rect,
+                              content_size,
                               clip_scroll_tree);
 
         topmost_scrolling_node_id
     }
 
-    pub fn add_clip_scroll_node(&mut self,
-                                new_node_id: ClipId,
-                                parent_id: ClipId,
-                                pipeline_id: PipelineId,
-                                content_rect: &LayerRect,
-                                clip_region: &ClipRegion,
-                                clip_scroll_tree: &mut ClipScrollTree) {
-        let clip_info = ClipInfo::new(clip_region,
-                                      &mut self.prim_store.gpu_data32,
-                                      PackedLayerIndex(self.packed_layers.len()));
-        let node = ClipScrollNode::new(pipeline_id,
-                                       parent_id,
-                                       content_rect,
-                                       &clip_region.main,
-                                       clip_info);
-
+    pub fn add_clip_node(&mut self,
+                         new_node_id: ClipId,
+                         parent_id: ClipId,
+                         pipeline_id: PipelineId,
+                         clip_region: ClipRegion,
+                         clip_scroll_tree: &mut ClipScrollTree) {
+        let clip_info = ClipInfo::new(clip_region, PackedLayerIndex(self.packed_layers.len()));
+        let node = ClipScrollNode::new(pipeline_id, parent_id, clip_info);
         clip_scroll_tree.add_node(node, new_node_id);
         self.packed_layers.push(PackedLayer::empty());
     }
 
     pub fn add_scroll_frame(&mut self,
                             new_node_id: ClipId,
                             parent_id: ClipId,
                             pipeline_id: PipelineId,
-                            content_rect: &LayerRect,
                             frame_rect: &LayerRect,
+                            content_size: &LayerSize,
                             clip_scroll_tree: &mut ClipScrollTree) {
         let node = ClipScrollNode::new_scroll_frame(pipeline_id,
                                                     parent_id,
-                                                    content_rect,
-                                                    frame_rect);
+                                                    frame_rect,
+                                                    content_size);
 
         clip_scroll_tree.add_node(node, new_node_id);
     }
 
     pub fn pop_reference_frame(&mut self) {
         self.reference_frame_stack.pop();
     }
 
     pub fn add_solid_rectangle(&mut self,
                                clip_and_scroll: ClipAndScrollInfo,
                                rect: &LayerRect,
-                               clip_region: &ClipRegion,
+                               local_clip: &LocalClip,
                                color: &ColorF,
                                flags: PrimitiveFlags) {
         if color.a == 0.0 {
             return;
         }
 
         let prim = RectanglePrimitive {
             color: *color,
         };
 
         let prim_index = self.add_primitive(clip_and_scroll,
                                             rect,
-                                            clip_region,
+                                            local_clip,
                                             &[],
                                             PrimitiveContainer::Rectangle(prim));
 
         match flags {
             PrimitiveFlags::None => {}
             PrimitiveFlags::Scrollbar(clip_id, border_radius) => {
                 self.scrollbar_prims.push(ScrollbarPrimitive {
-                    prim_index: prim_index,
-                    clip_id: clip_id,
-                    border_radius: border_radius,
+                    prim_index,
+                    clip_id,
+                    border_radius,
                 });
             }
         }
     }
 
     pub fn add_border(&mut self,
                       clip_and_scroll: ClipAndScrollInfo,
                       rect: LayerRect,
-                      clip_region: &ClipRegion,
+                      local_clip: &LocalClip,
                       border_item: &BorderDisplayItem,
                       gradient_stops: ItemRange<GradientStop>,
                       gradient_stops_count: usize) {
         let create_segments = |outset: SideOffsets2D<f32>| {
             // Calculate the modified rect as specific by border-image-outset
             let origin = LayerPoint::new(rect.origin.x - outset.left,
                                          rect.origin.y - outset.top);
             let size = LayerSize::new(rect.size.width + outset.left + outset.right,
@@ -539,16 +528,25 @@ impl FrameBuilder {
 
                     // Bottom left
                     ImageBorderSegment::new(LayerRect::from_floats(bl_outer.x, bl_inner.y, bl_inner.x, bl_outer.y),
                                             TexelRect::new(px0, py2, px1, py3),
                                             RepeatMode::Stretch,
                                             RepeatMode::Stretch),
                 ];
 
+                // Center
+                if border.fill {
+                    segments.push(ImageBorderSegment::new(
+                        LayerRect::from_floats(tl_inner.x, tl_inner.y, tr_inner.x, bl_inner.y),
+                        TexelRect::new(px1, py1, px2, py2),
+                        border.repeat_horizontal,
+                        border.repeat_vertical))
+                }
+
                 // Add edge segments if valid size.
                 if px1 < px2 && py1 < py2 {
                     segments.extend_from_slice(&[
                         // Top
                         ImageBorderSegment::new(LayerRect::from_floats(tl_inner.x, tl_outer.y, tr_inner.x, tl_inner.y),
                                                 TexelRect::new(px1, py0, px2, py1),
                                                 border.repeat_horizontal,
                                                 RepeatMode::Stretch),
@@ -571,55 +569,55 @@ impl FrameBuilder {
                                                 RepeatMode::Stretch,
                                                 border.repeat_vertical),
                     ]);
                 }
 
                 for segment in segments {
                     self.add_image(clip_and_scroll,
                                    segment.geom_rect,
-                                   clip_region,
+                                   local_clip,
                                    &segment.stretch_size,
                                    &segment.tile_spacing,
                                    Some(segment.sub_rect),
                                    border.image_key,
                                    ImageRendering::Auto,
                                    None);
                 }
             }
             BorderDetails::Normal(ref border) => {
                 self.add_normal_border(&rect,
                                        border,
                                        &border_item.widths,
                                        clip_and_scroll,
-                                       clip_region);
+                                       local_clip);
             }
             BorderDetails::Gradient(ref border) => {
                 for segment in create_segments(border.outset) {
                     let segment_rel = segment.origin - rect.origin;
 
                     self.add_gradient(clip_and_scroll,
                                       segment,
-                                      clip_region,
+                                      local_clip,
                                       border.gradient.start_point - segment_rel,
                                       border.gradient.end_point - segment_rel,
                                       gradient_stops,
                                       gradient_stops_count,
                                       border.gradient.extend_mode,
                                       segment.size,
                                       LayerSize::zero());
                 }
             }
             BorderDetails::RadialGradient(ref border) => {
                 for segment in create_segments(border.outset) {
                     let segment_rel = segment.origin - rect.origin;
 
                     self.add_radial_gradient(clip_and_scroll,
                                              segment,
-                                             clip_region,
+                                             local_clip,
                                              border.gradient.start_center - segment_rel,
                                              border.gradient.start_radius,
                                              border.gradient.end_center - segment_rel,
                                              border.gradient.end_radius,
                                              border.gradient.ratio_xy,
                                              gradient_stops,
                                              border.gradient.extend_mode,
                                              segment.size,
@@ -627,17 +625,17 @@ impl FrameBuilder {
                 }
             }
         }
     }
 
     pub fn add_gradient(&mut self,
                         clip_and_scroll: ClipAndScrollInfo,
                         rect: LayerRect,
-                        clip_region: &ClipRegion,
+                        local_clip: &LocalClip,
                         start_point: LayerPoint,
                         end_point: LayerPoint,
                         stops: ItemRange<GradientStop>,
                         stops_count: usize,
                         extend_mode: ExtendMode,
                         tile_size: LayerSize,
                         tile_spacing: LayerSize) {
         let tile_repeat = tile_size + tile_spacing;
@@ -671,73 +669,72 @@ impl FrameBuilder {
         let (sp, ep) = if reverse_stops {
             (end_point, start_point)
         } else {
             (start_point, end_point)
         };
 
         let gradient_cpu = GradientPrimitiveCpu {
             stops_range: stops,
-            stops_count: stops_count,
-            extend_mode: extend_mode,
-            reverse_stops: reverse_stops,
+            stops_count,
+            extend_mode,
+            reverse_stops,
             gpu_blocks: [
                 [sp.x, sp.y, ep.x, ep.y].into(),
                 [tile_size.width, tile_size.height, tile_repeat.width, tile_repeat.height].into(),
                 [pack_as_float(extend_mode as u32), 0.0, 0.0, 0.0].into(),
             ],
         };
 
         let prim = if aligned {
             PrimitiveContainer::AlignedGradient(gradient_cpu)
         } else {
             PrimitiveContainer::AngleGradient(gradient_cpu)
         };
 
-        self.add_primitive(clip_and_scroll, &rect, clip_region, &[], prim);
+        self.add_primitive(clip_and_scroll, &rect, local_clip, &[], prim);
     }
 
     pub fn add_radial_gradient(&mut self,
                                clip_and_scroll: ClipAndScrollInfo,
                                rect: LayerRect,
-                               clip_region: &ClipRegion,
+                               local_clip: &LocalClip,
                                start_center: LayerPoint,
                                start_radius: f32,
                                end_center: LayerPoint,
                                end_radius: f32,
                                ratio_xy: f32,
                                stops: ItemRange<GradientStop>,
                                extend_mode: ExtendMode,
                                tile_size: LayerSize,
                                tile_spacing: LayerSize) {
         let tile_repeat = tile_size + tile_spacing;
 
         let radial_gradient_cpu = RadialGradientPrimitiveCpu {
             stops_range: stops,
-            extend_mode: extend_mode,
-            gpu_data_address: GpuStoreAddress(0),
+            extend_mode,
             gpu_data_count: 0,
             gpu_blocks: [
                 [start_center.x, start_center.y, end_center.x, end_center.y].into(),
                 [start_radius, end_radius, ratio_xy, pack_as_float(extend_mode as u32)].into(),
                 [tile_size.width, tile_size.height, tile_repeat.width, tile_repeat.height].into(),
             ],
         };
 
         self.add_primitive(clip_and_scroll,
                            &rect,
-                           clip_region,
+                           local_clip,
                            &[],
                            PrimitiveContainer::RadialGradient(radial_gradient_cpu));
     }
 
     pub fn add_text(&mut self,
                     clip_and_scroll: ClipAndScrollInfo,
                     rect: LayerRect,
-                    clip_region: &ClipRegion,
+                    local_clip: &LocalClip,
                     font_key: FontKey,
                     size: Au,
                     blur_radius: f32,
                     color: &ColorF,
                     glyph_range: ItemRange<GlyphInstance>,
                     glyph_count: usize,
                     glyph_options: Option<GlyphOptions>) {
         if color.a == 0.0 {
@@ -759,89 +756,88 @@ impl FrameBuilder {
         // There are some conditions under which we can't use
         // subpixel text rendering, even if enabled.
         if render_mode == FontRenderMode::Subpixel {
             // text-blur shadow needs to force alpha AA.
             if blur_radius != 0.0 {
                 render_mode = FontRenderMode::Alpha;
             }
 
+            if color.a != 1.0 {
+                render_mode = FontRenderMode::Alpha;
+            }
+
             // text on a stacking context that has filters
             // (e.g. opacity) can't use sub-pixel.
             // TODO(gw): It's possible we can relax this in
             //           the future, if we modify the way
             //           we handle subpixel blending.
             if let Some(sc_index) = self.stacking_context_stack.last() {
                 let stacking_context = &self.stacking_context_store[sc_index.0];
                 if stacking_context.composite_ops.count() > 0 {
                     render_mode = FontRenderMode::Alpha;
                 }
             }
         }
 
         let prim_cpu = TextRunPrimitiveCpu {
-            font_key: font_key,
+            font_key,
             logical_font_size: size,
-            blur_radius: blur_radius,
-            glyph_range: glyph_range,
-            glyph_count: glyph_count,
+            blur_radius,
+            glyph_range,
+            glyph_count,
             glyph_instances: Vec::new(),
             color: *color,
-            render_mode: render_mode,
-            glyph_options: glyph_options,
+            render_mode,
+            glyph_options,
         };
 
         self.add_primitive(clip_and_scroll,
                            &rect,
-                           clip_region,
+                           local_clip,
                            &[],
                            PrimitiveContainer::TextRun(prim_cpu));
     }
 
     pub fn fill_box_shadow_rect(&mut self,
                                 clip_and_scroll: ClipAndScrollInfo,
                                 box_bounds: &LayerRect,
                                 bs_rect: LayerRect,
-                                clip_region: &ClipRegion,
+                                local_clip: &LocalClip,
                                 color: &ColorF,
                                 border_radius: f32,
                                 clip_mode: BoxShadowClipMode) {
         // We can draw a rectangle instead with the proper border radius clipping.
         let (bs_clip_mode, rect_to_draw) = match clip_mode {
             BoxShadowClipMode::Outset |
             BoxShadowClipMode::None => (ClipMode::Clip, bs_rect),
             BoxShadowClipMode::Inset => (ClipMode::ClipOut, *box_bounds),
         };
 
         let box_clip_mode = !bs_clip_mode;
 
-        // Clip the inside
-        let extra_clips = [ClipSource::Complex(bs_rect,
-                                               border_radius,
-                                               bs_clip_mode),
-                           // Clip the outside of the box
-                           ClipSource::Complex(*box_bounds,
-                                             border_radius,
-                                             box_clip_mode)];
+        // Clip the inside and then the outside of the box.
+        let extra_clips = [ClipSource::Complex(bs_rect, border_radius, bs_clip_mode),
+                           ClipSource::Complex(*box_bounds, border_radius, box_clip_mode)];
 
         let prim = RectanglePrimitive {
             color: *color,
         };
 
         self.add_primitive(clip_and_scroll,
                            &rect_to_draw,
-                           clip_region,
+                           local_clip,
                            &extra_clips,
                            PrimitiveContainer::Rectangle(prim));
     }
 
     pub fn add_box_shadow(&mut self,
                           clip_and_scroll: ClipAndScrollInfo,
                           box_bounds: &LayerRect,
-                          clip_region: &ClipRegion,
+                          local_clip: &LocalClip,
                           box_offset: &LayerVector2D,
                           color: &ColorF,
                           blur_radius: f32,
                           spread_radius: f32,
                           border_radius: f32,
                           clip_mode: BoxShadowClipMode) {
         if color.a == 0.0 {
             return
@@ -861,27 +857,27 @@ impl FrameBuilder {
         // Have to explicitly check this since euclid::TypedRect relies on negative rects
         let bs_rect_empty = bs_rect.size.width <= 0.0 || bs_rect.size.height <= 0.0;
 
         // Just draw a rectangle
         if (blur_radius == 0.0 && spread_radius == 0.0 && clip_mode == BoxShadowClipMode::None)
            || bs_rect_empty {
             self.add_solid_rectangle(clip_and_scroll,
                                      box_bounds,
-                                     clip_region,
+                                     local_clip,
                                      color,
                                      PrimitiveFlags::None);
             return;
         }
 
         if blur_radius == 0.0 && border_radius != 0.0 {
             self.fill_box_shadow_rect(clip_and_scroll,
                                       box_bounds,
                                       bs_rect,
-                                      clip_region,
+                                      local_clip,
                                       color,
                                       border_radius,
                                       clip_mode);
             return;
         }
 
         // Get the outer rectangle, based on the blur radius.
         let outside_edge_size = 2.0 * blur_radius;
@@ -944,28 +940,28 @@ impl FrameBuilder {
             }
         };
 
         match shadow_kind {
             BoxShadowKind::Simple(rects) => {
                 for rect in &rects {
                     self.add_solid_rectangle(clip_and_scroll,
                                              rect,
-                                             clip_region,
+                                             local_clip,
                                              color,
                                              PrimitiveFlags::None)
                 }
             }
             BoxShadowKind::Shadow(rects) => {
                 assert!(blur_radius > 0.0);
                 if clip_mode == BoxShadowClipMode::Inset {
                     self.fill_box_shadow_rect(clip_and_scroll,
                                               box_bounds,
                                               bs_rect,
-                                              clip_region,
+                                              local_clip,
                                               color,
                                               border_radius,
                                               clip_mode);
                 }
 
                 let inverted = match clip_mode {
                     BoxShadowClipMode::Outset | BoxShadowClipMode::None => 0.0,
                     BoxShadowClipMode::Inset => 1.0,
@@ -982,56 +978,56 @@ impl FrameBuilder {
                 if border_radius >= 0.0 {
                     extra_clips.push(ClipSource::Complex(*box_bounds,
                                                 border_radius,
                                                 extra_clip_mode));
                 }
 
                 let prim_cpu = BoxShadowPrimitiveCpu {
                     src_rect: *box_bounds,
-                    bs_rect: bs_rect,
+                    bs_rect,
                     color: *color,
-                    blur_radius: blur_radius,
-                    border_radius: border_radius,
-                    edge_size: edge_size,
-                    inverted: inverted,
-                    rects: rects,
+                    blur_radius,
+                    border_radius,
+                    edge_size,
+                    inverted,
+                    rects,
                 };
 
                 self.add_primitive(clip_and_scroll,
                                    &outer_rect,
-                                   clip_region,
+                                   local_clip,
                                    extra_clips.as_slice(),
                                    PrimitiveContainer::BoxShadow(prim_cpu));
             }
         }
     }
 
     pub fn add_webgl_rectangle(&mut self,
                                clip_and_scroll: ClipAndScrollInfo,
                                rect: LayerRect,
-                               clip_region: &ClipRegion,
+                               local_clip: &LocalClip,
                                context_id: WebGLContextId) {
         let prim_cpu = ImagePrimitiveCpu {
             kind: ImagePrimitiveKind::WebGL(context_id),
             gpu_blocks: [ [rect.size.width, rect.size.height, 0.0, 0.0].into(),
                           TexelRect::invalid().into() ],
         };
 
         self.add_primitive(clip_and_scroll,
                            &rect,
-                           clip_region,
+                           local_clip,
                            &[],
                            PrimitiveContainer::Image(prim_cpu));
     }
 
     pub fn add_image(&mut self,
                      clip_and_scroll: ClipAndScrollInfo,
                      rect: LayerRect,
-                     clip_region: &ClipRegion,
+                     local_clip: &LocalClip,
                      stretch_size: &LayerSize,
                      tile_spacing: &LayerSize,
                      sub_rect: Option<TexelRect>,
                      image_key: ImageKey,
                      image_rendering: ImageRendering,
                      tile: Option<TileOffset>) {
         let sub_rect_block = sub_rect.unwrap_or(TexelRect::invalid()).into();
 
@@ -1045,48 +1041,48 @@ impl FrameBuilder {
                             tile_spacing.width,
                             tile_spacing.height ].into(),
                             sub_rect_block,
                         ],
         };
 
         self.add_primitive(clip_and_scroll,
                            &rect,
-                           clip_region,
+                           local_clip,
                            &[],
                            PrimitiveContainer::Image(prim_cpu));
     }
 
     pub fn add_yuv_image(&mut self,
                          clip_and_scroll: ClipAndScrollInfo,
                          rect: LayerRect,
-                         clip_region: &ClipRegion,
+                         clip_rect: &LocalClip,
                          yuv_data: YuvData,
                          color_space: YuvColorSpace,
                          image_rendering: ImageRendering) {
         let format = yuv_data.get_format();
         let yuv_key = match yuv_data {
             YuvData::NV12(plane_0, plane_1) => [plane_0, plane_1, ImageKey::new(0, 0)],
             YuvData::PlanarYCbCr(plane_0, plane_1, plane_2) =>
                 [plane_0, plane_1, plane_2],
             YuvData::InterleavedYCbCr(plane_0) =>
                 [plane_0, ImageKey::new(0, 0), ImageKey::new(0, 0)],
         };
 
         let prim_cpu = YuvImagePrimitiveCpu {
-            yuv_key: yuv_key,
-            format: format,
-            color_space: color_space,
-            image_rendering: image_rendering,
+            yuv_key,
+            format,
+            color_space,
+            image_rendering,
             gpu_block: [rect.size.width, rect.size.height, 0.0, 0.0].into(),
         };
 
         self.add_primitive(clip_and_scroll,
                            &rect,
-                           clip_region,
+                           clip_rect,
                            &[],
                            PrimitiveContainer::YuvImage(prim_cpu));
     }
 
     /// Compute the contribution (bounding rectangles, and resources) of layers and their
     /// primitives in screen space.
     fn build_layer_screen_rects_and_cull_layers(&mut self,
                                                 screen_rect: &DeviceIntRect,
@@ -1172,18 +1168,18 @@ impl FrameBuilder {
         // and then actually populate with items and dependencies on the way up.
         let mut alpha_task_stack = Vec::new();
         // A map of "preserve-3d" contexts. We are baking these into render targets
         // and only compositing once we are out of "preserve-3d" hierarchy.
         // The stacking contexts that fall into this category are
         //  - ones with `ContextIsolation::Items`, for their actual items to be backed
         //  - immediate children of `ContextIsolation::Items`
         let mut preserve_3d_map: HashMap<StackingContextIndex, RenderTask> = HashMap::new();
-        // The plane splitter, using a simple BSP tree.
-        let mut splitter = BspSplitter::new();
+        // The plane splitter stack, using a simple BSP tree.
+        let mut splitter_stack = Vec::new();
 
         debug!("build_render_task()");
 
         for cmd in &self.cmds {
             match *cmd {
                 PrimitiveRunCmd::PushStackingContext(stacking_context_index) => {
                     let parent_isolation = sc_stack.last()
                                                    .map(|index| self.stacking_context_store[index.0].isolation);
@@ -1202,28 +1198,32 @@ impl FrameBuilder {
                     if stacking_context.isolation == ContextIsolation::Full && composite_count == 0 {
                         alpha_task_stack.push(current_task);
                         current_task = RenderTask::new_dynamic_alpha_batch(next_task_index, stacking_context_rect);
                         next_task_index.0 += 1;
                     }
 
                     if parent_isolation == Some(ContextIsolation::Items) ||
                        stacking_context.isolation == ContextIsolation::Items {
+                        if parent_isolation != Some(ContextIsolation::Items) {
+                            splitter_stack.push(BspSplitter::new());
+                        }
                         alpha_task_stack.push(current_task);
                         current_task = RenderTask::new_dynamic_alpha_batch(next_task_index, stacking_context_rect);
                         next_task_index.0 += 1;
                         //Note: technically, we shouldn't make a new alpha task for "preserve-3d" contexts
                         // that have no child items (only other stacking contexts). However, we don't know if
                         // there are any items at this time (in `PushStackingContext`).
                         //Note: the reason we add the polygon for splitting during `Push*` as opposed to `Pop*`
                         // is because we need to preserve the order of drawing for planes that match together.
-                        let scroll_node = clip_scroll_tree.nodes.get(&stacking_context.reference_frame_id).unwrap();
-                        let sc_polygon = make_polygon(stacking_context, scroll_node, stacking_context_index.0);
-                        debug!("\tadd {:?} -> {:?}", stacking_context_index, sc_polygon);
-                        splitter.add(sc_polygon);
+                        let frame_node = clip_scroll_tree.nodes.get(&stacking_context.reference_frame_id).unwrap();
+                        let sc_polygon = make_polygon(stacking_context, frame_node, stacking_context_index.0);
+                        debug!("\tsplitter[{}]: add {:?} -> {:?} with bounds {:?}", splitter_stack.len(),
+                            stacking_context_index, sc_polygon, stacking_context.isolated_items_bounds);
+                        splitter_stack.last_mut().unwrap().add(sc_polygon);
                     }
 
                     for _ in 0..composite_count {
                         alpha_task_stack.push(current_task);
                         current_task = RenderTask::new_dynamic_alpha_batch(next_task_index, stacking_context_rect);
                         next_task_index.0 += 1;
                     }
                 }
@@ -1286,53 +1286,55 @@ impl FrameBuilder {
                        stacking_context.isolation == ContextIsolation::Items {
                         //Note: we don't register the dependent tasks here. It's only done
                         // when we are out of the `preserve-3d` branch (see the code below),
                         // since this is only where the parent task is known.
                         preserve_3d_map.insert(stacking_context_index, current_task);
                         current_task = alpha_task_stack.pop().unwrap();
                     }
 
-                    if !preserve_3d_map.is_empty() && parent_isolation != Some(ContextIsolation::Items) {
+                    if parent_isolation != Some(ContextIsolation::Items) &&
+                       stacking_context.isolation == ContextIsolation::Items {
+                        debug!("\tsplitter[{}]: flush {:?}", splitter_stack.len(), current_task.id);
+                        let mut splitter = splitter_stack.pop().unwrap();
                         // Flush the accumulated plane splits onto the task tree.
                         // Notice how this is done before splitting in order to avoid duplicate tasks.
                         current_task.children.extend(preserve_3d_map.values().cloned());
-                        debug!("\tplane splitting in {:?}", current_task.id);
                         // Z axis is directed at the screen, `sort` is ascending, and we need back-to-front order.
                         for poly in splitter.sort(vec3(0.0, 0.0, 1.0)) {
                             let sc_index = StackingContextIndex(poly.anchor);
                             let task_id = preserve_3d_map[&sc_index].id;
                             debug!("\t\tproduce {:?} -> {:?} for {:?}", sc_index, poly, task_id);
                             let pp = &poly.points;
                             let gpu_blocks = [
                                 [pp[0].x, pp[0].y, pp[0].z, pp[1].x].into(),
                                 [pp[1].y, pp[1].z, pp[2].x, pp[2].y].into(),
                                 [pp[2].z, pp[3].x, pp[3].y, pp[3].z].into(),
                             ];
                             let handle = gpu_cache.push_per_frame_blocks(&gpu_blocks);
                             let item = AlphaRenderItem::SplitComposite(sc_index, task_id, handle, next_z);
                             current_task.as_alpha_batch().items.push(item);
                         }
-                        splitter.reset();
                         preserve_3d_map.clear();
                         next_z += 1;
                     }
                 }
                 PrimitiveRunCmd::PrimitiveRun(first_prim_index, prim_count, clip_and_scroll) => {
                     let stacking_context_index = *sc_stack.last().unwrap();
                     let stacking_context = &self.stacking_context_store[stacking_context_index.0];
 
                     if !stacking_context.is_visible {
                         continue;
                     }
 
                     let stacking_context_index = *sc_stack.last().unwrap();
                     let group_index = self.stacking_context_store[stacking_context_index.0]
                                           .clip_scroll_group(clip_and_scroll);
                     if self.clip_scroll_group_store[group_index.0].screen_bounding_rect.is_none() {
+                        debug!("\tcs-group {:?} screen rect is None", group_index);
                         continue
                     }
 
                     debug!("\trun of {} items into {:?}", prim_count, current_task.id);
 
                     for i in 0..prim_count {
                         let prim_index = PrimitiveIndex(first_prim_index.0 + i);
 
@@ -1421,66 +1423,71 @@ impl FrameBuilder {
                                         index == required_pass_count-1,
                                         cache_size));
         }
 
         main_render_task.assign_to_passes(passes.len() - 1, &mut passes);
 
         for pass in &mut passes {
             let ctx = RenderTargetContext {
-                device_pixel_ratio: device_pixel_ratio,
+                device_pixel_ratio,
                 stacking_context_store: &self.stacking_context_store,
                 clip_scroll_group_store: &self.clip_scroll_group_store,
                 prim_store: &self.prim_store,
-                resource_cache: resource_cache,
+                resource_cache,
             };
 
             pass.build(&ctx, gpu_cache, &mut render_tasks, &mut deferred_resolves);
 
             profile_counters.passes.inc();
             profile_counters.color_targets.add(pass.color_targets.target_count());
             profile_counters.alpha_targets.add(pass.alpha_targets.target_count());
         }
 
         let gpu_cache_updates = gpu_cache.end_frame(gpu_cache_profile);
 
         resource_cache.end_frame();
 
         Frame {
-            device_pixel_ratio: device_pixel_ratio,
+            device_pixel_ratio,
             background_color: self.background_color,
             window_size: self.screen_size,
-            profile_counters: profile_counters,
-            passes: passes,
-            cache_size: cache_size,
+            profile_counters,
+            passes,
+            cache_size,
             layer_texture_data: self.packed_layers.clone(),
             render_task_data: render_tasks.render_task_data,
-            gpu_data32: self.prim_store.gpu_data32.build(),
-            deferred_resolves: deferred_resolves,
+            deferred_resolves,
             gpu_cache_updates: Some(gpu_cache_updates),
         }
     }
 
 }
 
+#[derive(Debug, Clone, Copy)]
+struct LayerClipBounds {
+    outer: DeviceIntRect,
+    inner: DeviceIntRect,
+}
+
 struct LayerRectCalculationAndCullingPass<'a> {
     frame_builder: &'a mut FrameBuilder,
     screen_rect: &'a DeviceIntRect,
     clip_scroll_tree: &'a mut ClipScrollTree,
     display_lists: &'a DisplayListMap,
     resource_cache: &'a mut ResourceCache,
     gpu_cache: &'a mut GpuCache,
     profile_counters: &'a mut FrameProfileCounters,
     device_pixel_ratio: f32,
     stacking_context_stack: Vec<StackingContextIndex>,
 
     /// A cached clip info stack, which should handle the most common situation,
     /// which is that we are using the same clip info stack that we were using
     /// previously.
-    current_clip_stack: Vec<(PackedLayerIndex, MaskCacheInfo)>,
+    current_clip_stack: Vec<ClipWorkItem>,
 
     /// Information about the cached clip stack, which is used to avoid having
     /// to recalculate it for every primitive.
     current_clip_info: Option<(ClipId, Option<DeviceIntRect>)>
 }
 
 impl<'a> LayerRectCalculationAndCullingPass<'a> {
     fn create_and_run(frame_builder: &'a mut FrameBuilder,
@@ -1488,36 +1495,37 @@ impl<'a> LayerRectCalculationAndCullingP
                       clip_scroll_tree: &'a mut ClipScrollTree,
                       display_lists: &'a DisplayListMap,
                       resource_cache: &'a mut ResourceCache,
                       gpu_cache: &'a mut GpuCache,
                       profile_counters: &'a mut FrameProfileCounters,
                       device_pixel_ratio: f32) {
 
         let mut pass = LayerRectCalculationAndCullingPass {
-            frame_builder: frame_builder,
-            screen_rect: screen_rect,
-            clip_scroll_tree: clip_scroll_tree,
-            display_lists: display_lists,
-            resource_cache: resource_cache,
-            gpu_cache: gpu_cache,
-            profile_counters: profile_counters,
-            device_pixel_ratio: device_pixel_ratio,
+            frame_builder,
+            screen_rect,
+            clip_scroll_tree,
+            display_lists,
+            resource_cache,
+            gpu_cache,
+            profile_counters,
+            device_pixel_ratio,
             stacking_context_stack: Vec::new(),
             current_clip_stack: Vec::new(),
             current_clip_info: None,
         };
         pass.run();
     }
 
     fn run(&mut self) {
+        self.recalculate_clip_scroll_nodes();
         self.recalculate_clip_scroll_groups();
-        self.recalculate_clip_scroll_nodes();
         self.compute_stacking_context_visibility();
 
+        debug!("processing commands...");
         let commands = mem::replace(&mut self.frame_builder.cmds, Vec::new());
         for cmd in &commands {
             match *cmd {
                 PrimitiveRunCmd::PushStackingContext(stacking_context_index) =>
                     self.handle_push_stacking_context(stacking_context_index),
                 PrimitiveRunCmd::PrimitiveRun(prim_index, prim_count, clip_and_scroll) =>
                     self.handle_primitive_run(prim_index, prim_count, clip_and_scroll),
                 PrimitiveRunCmd::PopStackingContext => self.handle_pop_stacking_context(),
@@ -1537,82 +1545,91 @@ impl<'a> LayerRectCalculationAndCullingP
             let packed_layer_index = node_clip_info.packed_layer_index;
             let packed_layer = &mut self.frame_builder.packed_layers[packed_layer_index.0];
 
             // The coordinates of the mask are relative to the origin of the node itself,
             // so we need to account for that origin in the transformation we assign to
             // the packed layer.
             let transform = node.world_viewport_transform
                 .pre_translate(node.local_viewport_rect.origin.to_vector().to_3d());
-            packed_layer.set_transform(transform);
-
-            // Meanwhile, the combined viewport rect is relative to the reference frame, so
-            // we move it into the local coordinate system of the node.
-            let local_viewport_rect =
-                node.combined_local_viewport_rect.translate(&-node.local_viewport_rect.origin.to_vector());
 
-            node_clip_info.screen_bounding_rect = packed_layer.set_rect(&local_viewport_rect,
-                                                                        self.screen_rect,
-                                                                        self.device_pixel_ratio);
+            node_clip_info.screen_bounding_rect = if packed_layer.set_transform(transform) {
+                // Meanwhile, the combined viewport rect is relative to the reference frame, so
+                // we move it into the local coordinate system of the node.
+                let local_viewport_rect = node.combined_local_viewport_rect
+                    .translate(&-node.local_viewport_rect.origin.to_vector());
 
-            let mask_info = match node_clip_info.mask_cache_info {
-                Some(ref mut mask_info) => mask_info,
-                _ => continue,
+                packed_layer.set_rect(&local_viewport_rect,
+                                      self.screen_rect,
+                                      self.device_pixel_ratio)
+            } else {
+                None
             };
 
-            let display_list = self.display_lists.get(&node.pipeline_id)
-                                                 .expect("No display list?");
+            let inner_rect = match node_clip_info.screen_bounding_rect {
+                Some((_, rect)) => rect,
+                None => DeviceIntRect::zero(),
+            };
+            node_clip_info.screen_inner_rect = inner_rect;
 
-            mask_info.update(&node_clip_info.clip_sources,
-                             &packed_layer.transform,
-                             &mut self.frame_builder.prim_store.gpu_data32,
-                             self.device_pixel_ratio,
-                             display_list);
+            let bounds = node_clip_info.mask_cache_info.update(&node_clip_info.clip_sources,
+                                                               &transform,
+                                                               self.gpu_cache,
+                                                               self.device_pixel_ratio);
+
+            node_clip_info.screen_inner_rect = bounds.inner.as_ref()
+               .and_then(|inner| inner.device_rect.intersection(&inner_rect))
+               .unwrap_or(DeviceIntRect::zero());
 
             for clip_source in &node_clip_info.clip_sources {
                 if let Some(mask) = clip_source.image_mask() {
                     // We don't add the image mask for resolution, because
                     // layer masks are resolved later.
                     self.resource_cache.request_image(mask.image, ImageRendering::Auto, None);
                 }
             }
         }
     }
 
     fn recalculate_clip_scroll_groups(&mut self) {
+        debug!("recalculate_clip_scroll_groups");
         for ref mut group in &mut self.frame_builder.clip_scroll_group_store {
             let stacking_context_index = group.stacking_context_index;
             let stacking_context = &mut self.frame_builder
                                             .stacking_context_store[stacking_context_index.0];
 
             let scroll_node = &self.clip_scroll_tree.nodes[&group.scroll_node_id];
             let clip_node = &self.clip_scroll_tree.nodes[&group.clip_node_id];
             let packed_layer = &mut self.frame_builder.packed_layers[group.packed_layer_index.0];
 
             // The world content transform is relative to the containing reference frame,
             // so we translate into the origin of the stacking context itself.
             let transform = scroll_node.world_content_transform
                 .pre_translate(stacking_context.reference_frame_offset.to_3d());
-            packed_layer.set_transform(transform);
 
-            if !stacking_context.can_contribute_to_scene() {
+            if !packed_layer.set_transform(transform) || !stacking_context.can_contribute_to_scene() {
+                debug!("\t{:?} unable to set transform or contribute with {:?}",
+                    stacking_context_index, transform);
                 return;
             }
 
             // Here we move the viewport rectangle into the coordinate system
             // of the stacking context content.
-            let viewport_rect =
-                &clip_node.combined_local_viewport_rect
-                     .translate(&clip_node.reference_frame_relative_scroll_offset)
-                     .translate(&-scroll_node.reference_frame_relative_scroll_offset)
-                     .translate(&-stacking_context.reference_frame_offset)
-                     .translate(&-scroll_node.scroll_offset());
-            group.screen_bounding_rect = packed_layer.set_rect(viewport_rect,
+            let local_viewport_rect = clip_node.combined_local_viewport_rect
+                .translate(&clip_node.reference_frame_relative_scroll_offset)
+                .translate(&-scroll_node.reference_frame_relative_scroll_offset)
+                .translate(&-stacking_context.reference_frame_offset)
+                .translate(&-scroll_node.scroll_offset());
+
+            group.screen_bounding_rect = packed_layer.set_rect(&local_viewport_rect,
                                                                self.screen_rect,
                                                                self.device_pixel_ratio);
+
+            debug!("\t{:?} local viewport {:?} screen bound {:?}",
+                stacking_context_index, local_viewport_rect, group.screen_bounding_rect);
         }
     }
 
     fn compute_stacking_context_visibility(&mut self) {
         for context_index in 0..self.frame_builder.stacking_context_store.len() {
             let is_visible = {
                 // We don't take into account visibility of children here, so we must
                 // do that later.
@@ -1623,29 +1640,38 @@ impl<'a> LayerRectCalculationAndCullingP
             };
             self.frame_builder.stacking_context_store[context_index].is_visible = is_visible;
         }
     }
 
     fn handle_pop_stacking_context(&mut self) {
         let stacking_context_index = self.stacking_context_stack.pop().unwrap();
 
-        let (bounding_rect, is_visible) = {
+        let (bounding_rect, is_visible, is_preserve_3d, reference_frame_id, reference_frame_bounds) = {
             let stacking_context =
                 &mut self.frame_builder.stacking_context_store[stacking_context_index.0];
             stacking_context.screen_bounds = stacking_context.screen_bounds
                                                              .intersection(self.screen_rect)
                                                              .unwrap_or(DeviceIntRect::zero());
-            (stacking_context.screen_bounds.clone(), stacking_context.is_visible)
+            (stacking_context.screen_bounds.clone(),
+             stacking_context.is_visible,
+             stacking_context.isolation == ContextIsolation::Items,
+             stacking_context.reference_frame_id,
+             stacking_context.isolated_items_bounds.translate(&stacking_context.reference_frame_offset),
+            )
         };
 
         if let Some(ref mut parent_index) = self.stacking_context_stack.last_mut() {
             let parent = &mut self.frame_builder.stacking_context_store[parent_index.0];
             parent.screen_bounds = parent.screen_bounds.union(&bounding_rect);
-
+            // add children local bounds only for non-item-isolated contexts
+            if !is_preserve_3d && parent.reference_frame_id == reference_frame_id {
+                let child_bounds = reference_frame_bounds.translate(&-parent.reference_frame_offset);
+                parent.isolated_items_bounds = parent.isolated_items_bounds.union(&child_bounds);
+            }
             // The previous compute_stacking_context_visibility pass did not take into
             // account visibility of children, so we do that now.
             parent.is_visible = parent.is_visible || is_visible;
         }
     }
 
     fn handle_push_stacking_context(&mut self, stacking_context_index: StackingContextIndex) {
         self.stacking_context_stack.push(stacking_context_index);
@@ -1653,144 +1679,174 @@ impl<'a> LayerRectCalculationAndCullingP
         // Reset bounding rect to zero. We will calculate it as we collect primitives
         // from various scroll layers. In handle_pop_stacking_context , we use this to
         // calculate the device bounding rect. In the future, we could cache this during
         // the initial adding of items for the common case (where there is only a single
         // scroll layer for items in a stacking context).
         let stacking_context = &mut self.frame_builder
                                         .stacking_context_store[stacking_context_index.0];
         stacking_context.screen_bounds = DeviceIntRect::zero();
+        stacking_context.isolated_items_bounds = LayerRect::zero();
     }
 
-    fn rebuild_clip_info_stack_if_necessary(&mut self, id: ClipId) -> Option<DeviceIntRect> {
-        if let Some((current_scroll_id, bounding_rect)) = self.current_clip_info {
-            if current_scroll_id == id {
+    fn rebuild_clip_info_stack_if_necessary(&mut self, clip_id: ClipId) -> Option<DeviceIntRect> {
+        if let Some((current_id, bounding_rect)) = self.current_clip_info {
+            if current_id == clip_id {
                 return bounding_rect;
             }
         }
 
         // TODO(mrobinson): If we notice that this process is expensive, we can special-case
         // more common situations, such as moving from a child or a parent.
         self.current_clip_stack.clear();
-        let mut bounding_rect = None;
+        self.current_clip_info = Some((clip_id, None));
 
-        let mut current_id = Some(id);
+        let mut bounding_rect = *self.screen_rect;
+        let mut current_id = Some(clip_id);
+        // Indicates if the next non-reference-frame that we encounter needs to have its
+        // local combined clip rectangle backed into the clip mask.
+        let mut next_node_needs_region_mask = false;
         while let Some(id) = current_id {
             let node = &self.clip_scroll_tree.nodes.get(&id).unwrap();
             current_id = node.parent;
 
-            let clip_info = match node.node_type {
-                NodeType::Clip(ref clip) if clip.is_masking() => clip,
+            let clip = match node.node_type {
+                NodeType::ReferenceFrame(transform) => {
+                    // if the transform is non-aligned, bake the next LCCR into the clip mask
+                    next_node_needs_region_mask |= !transform.preserves_2d_axis_alignment();
+                    continue
+                },
+                NodeType::Clip(ref clip) if clip.mask_cache_info.is_masking() => clip,
                 _ => continue,
             };
 
-            if bounding_rect.is_none() {
-                bounding_rect = Some(match clip_info.screen_bounding_rect {
-                    Some((_kind, rect)) => rect,
-                    None => DeviceIntRect::zero(),
-                });
+            // apply the screen bounds of the clip node
+            //Note: these are based on the local combined viewport, so can be tighter
+            if let Some((_kind, ref screen_rect)) = clip.screen_bounding_rect {
+                bounding_rect = match bounding_rect.intersection(screen_rect) {
+                    Some(rect) => rect,
+                    None => return None,
+                }
             }
-            self.current_clip_stack.push((clip_info.packed_layer_index,
-                                          clip_info.mask_cache_info.clone().unwrap()))
+
+            let clip_info = if next_node_needs_region_mask {
+                clip.mask_cache_info.clone()
+            } else {
+                clip.mask_cache_info.strip_aligned()
+            };
+
+            // apply the outer device bounds of the clip stack
+            if let Some(ref outer) = clip_info.bounds.outer {
+                bounding_rect = match bounding_rect.intersection(&outer.device_rect) {
+                    Some(rect) => rect,
+                    None => return None,
+                }
+            }
+
+            //TODO-LCCR: bake a single LCCR instead of all aligned rects?
+            self.current_clip_stack.push((clip.packed_layer_index, clip_info));
+            next_node_needs_region_mask = false;
         }
+
         self.current_clip_stack.reverse();
-
-        self.current_clip_info = Some((id, bounding_rect));
-        bounding_rect
+        self.current_clip_info = Some((clip_id, Some(bounding_rect)));
+        Some(bounding_rect)
     }
 
     fn handle_primitive_run(&mut self,
-                            prim_index: PrimitiveIndex,
+                            base_prim_index: PrimitiveIndex,
                             prim_count: usize,
                             clip_and_scroll: ClipAndScrollInfo) {
         let stacking_context_index = *self.stacking_context_stack.last().unwrap();
         let (packed_layer_index, pipeline_id) = {
             let stacking_context =
                 &self.frame_builder.stacking_context_store[stacking_context_index.0];
 
             if !stacking_context.is_visible {
+                debug!("{:?} of invisible {:?}", base_prim_index, stacking_context_index);
                 return;
             }
 
             let group_index = stacking_context.clip_scroll_group(clip_and_scroll);
             let clip_scroll_group = &self.frame_builder.clip_scroll_group_store[group_index.0];
-            (clip_scroll_group.packed_layer_index, stacking_context.pipeline_id)
+            (clip_scroll_group.packed_layer_index,
+             stacking_context.pipeline_id)
         };
 
-        let clip_bounds =
-            self.rebuild_clip_info_stack_if_necessary(clip_and_scroll.clip_node_id());
-        if clip_bounds.map_or(false, |bounds| bounds.is_empty()) {
-            return;
-        }
+        debug!("\t{:?} of {:?} at {:?}", base_prim_index, stacking_context_index, packed_layer_index);
+        let clip_bounds = match self.rebuild_clip_info_stack_if_necessary(clip_and_scroll.clip_node_id()) {
+            Some(rect) => rect,
+            None => return,
+        };
 
         let stacking_context =
             &mut self.frame_builder.stacking_context_store[stacking_context_index.0];
         let packed_layer = &self.frame_builder.packed_layers[packed_layer_index.0];
         let display_list = self.display_lists.get(&pipeline_id)
                                              .expect("No display list?");
-        for i in 0..prim_count {
-            let prim_index = PrimitiveIndex(prim_index.0 + i);
-            if self.frame_builder.prim_store.build_bounding_rect(prim_index,
-                                                                 self.screen_rect,
-                                                                 &packed_layer.transform,
-                                                                 &packed_layer.local_clip_rect,
-                                                                 self.device_pixel_ratio) {
-                self.frame_builder.prim_store.prepare_prim_for_render(prim_index,
-                                                                      self.resource_cache,
-                                                                      self.gpu_cache,
-                                                                      &packed_layer.transform,
-                                                                      self.device_pixel_ratio,
-                                                                      display_list);
+        debug!("\tclip_bounds {:?}, layer_local_clip {:?}", clip_bounds, packed_layer.local_clip_rect);
 
-                // If the primitive is visible, consider culling it via clip rect(s).
-                // If it is visible but has clips, create the clip task for it.
-                let prim_bounding_rect =
-                    match self.frame_builder.prim_store.cpu_bounding_rects[prim_index.0] {
-                    Some(rect) => rect,
-                    _ => continue,
-                };
+        for i in 0..prim_count {
+            let prim_index = PrimitiveIndex(base_prim_index.0 + i);
+            let prim_store = &mut self.frame_builder.prim_store;
+            let (prim_local_rect, prim_screen_rect) = match prim_store
+                .build_bounding_rect(prim_index,
+                                     &clip_bounds,
+                                     &packed_layer.transform,
+                                     &packed_layer.local_clip_rect,
+                                     self.device_pixel_ratio) {
+                Some(rects) => rects,
+                None => continue,
+            };
 
-                let prim_metadata = &mut self.frame_builder.prim_store.cpu_metadata[prim_index.0];
-                let prim_clip_info = prim_metadata.clip_cache_info.as_ref();
-                let mut visible = true;
+            debug!("\t\t{:?} bound is {:?}", prim_index, prim_screen_rect);
 
-                stacking_context.screen_bounds =
-                    stacking_context.screen_bounds.union(&prim_bounding_rect);
+            let prim_metadata = prim_store.prepare_prim_for_render(prim_index,
+                                                                   self.resource_cache,
+                                                                   self.gpu_cache,
+                                                                   &packed_layer.transform,
+                                                                   self.device_pixel_ratio,
+                                                                   display_list);
 
-                if let Some(info) = prim_clip_info {
-                    self.current_clip_stack.push((packed_layer_index, info.clone()));
-                }
+            stacking_context.screen_bounds = stacking_context.screen_bounds.union(&prim_screen_rect);
+            stacking_context.isolated_items_bounds = stacking_context.isolated_items_bounds.union(&prim_local_rect);
 
-                // Try to create a mask if we may need to.
-                if !self.current_clip_stack.is_empty() {
-                    // If the primitive doesn't have a specific clip, key the task ID off the
-                    // stacking context. This means that two primitives which are only clipped
-                    // by the stacking context stack can share clip masks during render task
-                    // assignment to targets.
-                    let clip_bounds = clip_bounds.unwrap_or_else(DeviceIntRect::zero);
-                    let (mask_key, mask_rect) = match prim_clip_info {
-                        Some(..) => (MaskCacheKey::Primitive(prim_index), prim_bounding_rect),
-                        None => (MaskCacheKey::ClipNode(clip_and_scroll.clip_node_id()),
-                                                        clip_bounds)
-                    };
-                    let mask_opt =
-                        RenderTask::new_mask(mask_rect, mask_key, &self.current_clip_stack);
-                    match mask_opt {
-                        MaskResult::Outside => { // Primitive is completely clipped out.
-                            prim_metadata.clip_task = None;
-                            self.frame_builder.prim_store.cpu_bounding_rects[prim_index.0] = None;
-                            visible = false;
-                        }
-                        MaskResult::Inside(task) => prim_metadata.clip_task = Some(task),
+            // Try to create a mask if we may need to.
+            if !self.current_clip_stack.is_empty() || prim_metadata.clip_cache_info.is_some() {
+                // If the primitive doesn't have a specific clip, key the task ID off the
+                // stacking context. This means that two primitives which are only clipped
+                // by the stacking context stack can share clip masks during render task
+                // assignment to targets.
+                let (mask_key, mask_rect, extra) = match prim_metadata.clip_cache_info {
+                    Some(ref info) => {
+                        // Take into account the actual clip info of the primitive, and
+                        // mutate the current bounds accordingly.
+                        let mask_rect = match info.bounds.outer {
+                            Some(ref outer) => {
+                                match prim_screen_rect.intersection(&outer.device_rect) {
+                                    Some(rect) => rect,
+                                    None => continue,
+                                }
+                            }
+                            _ => prim_screen_rect,
+                        };
+                        (MaskCacheKey::Primitive(prim_index),
+                         mask_rect,
+                         Some((packed_layer_index, info.strip_aligned())))
                     }
-                }
+                    None => {
+                        //Note: can't use `prim_bounding_rect` since
+                        // the primitive ID is not a part of the task key
+                        (MaskCacheKey::ClipNode(clip_and_scroll.clip_node_id()),
+                         clip_bounds,
+                         None)
+                    }
+                };
+                prim_metadata.clip_task = RenderTask::new_mask(mask_rect,
+                                                               mask_key,
+                                                               &self.current_clip_stack,
+                                                               extra)
+            }
 
-                if prim_clip_info.is_some() {
-                    self.current_clip_stack.pop();
-                }
-
-                if visible {
-                    self.profile_counters.visible_primitives.inc();
-                }
-            }
+            self.profile_counters.visible_primitives.inc();
         }
     }
 }
--- a/gfx/webrender/src/glyph_rasterizer.rs
+++ b/gfx/webrender/src/glyph_rasterizer.rs
@@ -13,20 +13,20 @@ use rayon::prelude::*;
 use resource_cache::ResourceClassCache;
 use std::hash::BuildHasherDefault;
 use std::sync::{Arc, Mutex, MutexGuard};
 use std::sync::mpsc::{channel, Receiver, Sender};
 use std::collections::hash_map::Entry;
 use std::collections::HashSet;
 use std::mem;
 use texture_cache::{TextureCacheItemId, TextureCache};
-use webrender_traits::FontTemplate;
-use webrender_traits::{FontKey, FontRenderMode, ImageData, ImageFormat};
-use webrender_traits::{ImageDescriptor, ColorF, LayoutPoint};
-use webrender_traits::{GlyphKey, GlyphOptions, GlyphInstance, GlyphDimensions};
+use api::FontTemplate;
+use api::{FontKey, FontRenderMode, ImageData, ImageFormat};
+use api::{ImageDescriptor, ColorF, LayoutPoint};
+use api::{GlyphKey, GlyphOptions, GlyphInstance, GlyphDimensions};
 
 pub type GlyphCache = ResourceClassCache<GlyphRequest, Option<TextureCacheItemId>>;
 
 pub struct FontContexts {
     // These worker are mostly accessed from their corresponding worker threads.
     // The goal is that there should be no noticeable contention on the muteces.
     worker_contexts: Vec<Mutex<FontContext>>,
 
@@ -109,20 +109,20 @@ impl GlyphRasterizer {
         GlyphRasterizer {
             font_contexts: Arc::new(
                 FontContexts {
                     worker_contexts: contexts,
                     shared_context: Mutex::new(FontContext::new()),
                     workers: Arc::clone(&workers),
                 }
             ),
-            glyph_rx: glyph_rx,
-            glyph_tx: glyph_tx,
+            glyph_rx,
+            glyph_tx,
             pending_glyphs: HashSet::new(),
-            workers: workers,
+            workers,
             fonts_to_remove: Vec::new(),
         }
     }
 
     pub fn add_font(&mut self, font_key: FontKey, template: FontTemplate) {
         let font_contexts = Arc::clone(&self.font_contexts);
         // It's important to synchronously add the font for the shared context because
         // we use it to check that fonts have been properly added when requesting glyphs.
@@ -332,18 +332,18 @@ impl GlyphRequest {
         color: ColorF,
         index: u32,
         point: LayoutPoint,
         render_mode: FontRenderMode,
         glyph_options: Option<GlyphOptions>,
     ) -> GlyphRequest {
         GlyphRequest {
             key: GlyphKey::new(font_key, size, color, index, point, render_mode),
-            render_mode: render_mode,
-            glyph_options: glyph_options,
+            render_mode,
+            glyph_options,
         }
     }
 }
 
 struct GlyphRasterJob {
     request: GlyphRequest,
     result: Option<RasterizedGlyph>,
 }
--- a/gfx/webrender/src/gpu_cache.rs
+++ b/gfx/webrender/src/gpu_cache.rs
@@ -24,17 +24,17 @@
 //! address in the GPU cache of a given resource slot
 //! for this frame.
 
 use device::FrameId;
 use internal_types::UvRect;
 use profiler::GpuCacheProfileCounters;
 use renderer::MAX_VERTEX_TEXTURE_WIDTH;
 use std::{mem, u32};
-use webrender_traits::{ColorF, LayerRect};
+use api::{ColorF, LayerRect};
 
 pub const GPU_CACHE_INITIAL_HEIGHT: u32 = 512;
 const FRAMES_BEFORE_EVICTION: usize = 10;
 const NEW_ROWS_PER_RESIZE: u32 = 512;
 
 #[derive(Debug, Copy, Clone, Eq, PartialEq)]
 struct Epoch(u32);
 
@@ -51,16 +51,24 @@ struct CacheLocation {
 }
 
 /// A single texel in RGBAF32 texture - 16 bytes.
 #[derive(Copy, Clone, Debug)]
 pub struct GpuBlockData {
     pub data: [f32; 4],
 }
 
+impl GpuBlockData {
+    pub fn empty() -> GpuBlockData {
+        GpuBlockData {
+            data: [0.0; 4],
+        }
+    }
+}
+
 /// Conversion helpers for GpuBlockData
 impl Into<GpuBlockData> for ColorF {
     fn into(self) -> GpuBlockData {
         GpuBlockData {
             data: [self.r, self.g, self.b, self.a],
         }
     }
 }
@@ -149,18 +157,18 @@ struct Block {
     last_access_time: FrameId,
 }
 
 impl Block {
     fn new(address: GpuCacheAddress,
            next: Option<BlockIndex>,
            frame_id: FrameId) -> Block {
         Block {
-            address: address,
-            next: next,
+            address,
+            next,
             last_access_time: frame_id,
             epoch: Epoch(0),
         }
     }
 }
 
 #[derive(Debug, Copy, Clone)]
 struct BlockIndex(usize);
@@ -172,17 +180,17 @@ struct Row {
     // This means no dealing with fragmentation within a cache
     // row as items are allocated and freed.
     block_count_per_item: usize,
 }
 
 impl Row {
     fn new(block_count_per_item: usize) -> Row {
         Row {
-            block_count_per_item: block_count_per_item,
+            block_count_per_item,
         }
     }
 }
 
 // A list of update operations that can be applied on the cache
 // this frame. The list of updates is created by the render backend
 // during frame construction. It's passed to the render thread
 // where GL commands can be applied.
@@ -343,17 +351,17 @@ impl Texture {
         self.occupied_list_head = Some(free_block_index);
         self.allocated_block_count += alloc_size;
 
         if let Some(pending_block_index) = pending_block_index {
             // Add this update to the pending list of blocks that need
             // to be updated on the GPU.
             self.updates.push(GpuCacheUpdate::Copy {
                 block_index: pending_block_index,
-                block_count: block_count,
+                block_count,
                 address: block.address,
             });
         }
 
         CacheLocation {
             block_index: free_block_index,
             epoch: block.epoch,
         }
@@ -426,23 +434,30 @@ impl Texture {
 pub struct GpuDataRequest<'a> {
     handle: &'a mut GpuCacheHandle,
     frame_id: FrameId,
     start_index: usize,
     texture: &'a mut Texture,
 }
 
 impl<'a> GpuDataRequest<'a> {
-    pub fn push(&mut self, block: GpuBlockData) {
-        self.texture.pending_blocks.push(block);
+    pub fn push<B>(&mut self, block: B)
+    where B: Into<GpuBlockData>
+    {
+        self.texture.pending_blocks.push(block.into());
     }
 
     pub fn extend_from_slice(&mut self, blocks: &[GpuBlockData]) {
         self.texture.pending_blocks.extend_from_slice(blocks);
     }
+
+    /// Consume the request and return the number of blocks written
+    pub fn close(self) -> usize {
+        self.texture.pending_blocks.len() - self.start_index
+    }
 }
 
 impl<'a> Drop for GpuDataRequest<'a> {
     fn drop(&mut self) {
         // Push the data to the texture pending updates list.
         let block_count = self.texture.pending_blocks.len() - self.start_index;
         let location = self.texture.push_data(Some(self.start_index),
                                               block_count,
@@ -494,17 +509,17 @@ impl GpuCache {
             if block.epoch == location.epoch {
                 // Mark last access time to avoid evicting this block.
                 block.last_access_time = self.frame_id;
                 return None
             }
         }
 
         Some(GpuDataRequest {
-            handle: handle,
+            handle,
             frame_id: self.frame_id,
             start_index: self.texture.pending_blocks.len(),
             texture: &mut self.texture,
         })
     }
 
     // Push an array of data blocks to be uploaded to the GPU
     // unconditionally for this frame. The cache handle will
deleted file mode 100644
--- a/gfx/webrender/src/gpu_store.rs
+++ /dev/null
@@ -1,158 +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 device::TextureFilter;
-use std::marker::PhantomData;
-use std::mem;
-use std::ops::Add;
-use util::recycle_vec;
-use webrender_traits::ImageFormat;
-
-#[derive(Debug, Copy, Clone, Eq, Hash, PartialEq)]
-pub struct GpuStoreAddress(pub i32);
-
-
-impl Add<i32> for GpuStoreAddress {
-    type Output = GpuStoreAddress;
-
-    fn add(self, other: i32) -> GpuStoreAddress {
-        GpuStoreAddress(self.0 + other)
-    }
-}
-
-impl Add<usize> for GpuStoreAddress {
-    type Output = GpuStoreAddress;
-
-    fn add(self, other: usize) -> GpuStoreAddress {
-        GpuStoreAddress(self.0 + other as i32)
-    }
-}
-
-pub trait GpuStoreLayout {
-    fn image_format() -> ImageFormat;
-
-    fn texture_width<T>() -> usize;
-
-    fn texture_filter() -> TextureFilter;
-
-    fn texel_size() -> usize {
-        match Self::image_format() {
-            ImageFormat::BGRA8 => 4,
-            ImageFormat::RGBAF32 => 16,
-            _ => unreachable!(),
-        }
-    }
-
-    fn texels_per_item<T>() -> usize {
-        let item_size = mem::size_of::<T>();
-        let texel_size = Self::texel_size();
-        debug_assert!(item_size % texel_size == 0);
-        item_size / texel_size
-    }
-
-    fn items_per_row<T>() -> usize {
-        Self::texture_width::<T>() / Self::texels_per_item::<T>()
-    }
-
-    fn rows_per_item<T>() -> usize {
-        Self::texels_per_item::<T>() / Self::texture_width::<T>()
-    }
-}
-
-/// A CPU-side buffer storing content to be uploaded to the GPU.
-pub struct GpuStore<T, L> {
-    data: Vec<T>,
-    layout: PhantomData<L>,
-    // TODO(gw): Could store this intrusively inside
-    // the data array free slots.
-    //free_list: Vec<GpuStoreAddress>,
-}
-
-impl<T: Clone + Default, L: GpuStoreLayout> GpuStore<T, L> {
-    pub fn new() -> GpuStore<T, L> {
-        GpuStore {
-            data: Vec::new(),
-            layout: PhantomData,
-            //free_list: Vec::new(),
-        }
-    }
-
-    pub fn recycle(self) -> Self {
-        GpuStore {
-            data: recycle_vec(self.data),
-            layout: PhantomData,
-        }
-    }
-
-    pub fn push<E>(&mut self, data: E) -> GpuStoreAddress where T: From<E> {
-        let address = GpuStoreAddress(self.data.len() as i32);
-        self.data.push(T::from(data));
-        address
-    }
-
-    // TODO(gw): Change this to do incremental updates, which means
-    // there is no need to copy all this data during every scroll!
-    pub fn build(&self) -> Vec<T> {
-        let items_per_row = L::items_per_row::<T>();
-
-        let mut items = self.data.clone();
-
-        // Extend the data array to be a multiple of the row size.
-        // This ensures memory safety when the array is passed to
-        // OpenGL to upload to the GPU.
-        if items_per_row != 0 {
-            while items_per_row != 0 && items.len() % items_per_row != 0 {
-                items.push(T::default());
-            }
-        }
-
-        items
-    }
-
-    pub fn alloc(&mut self, count: usize) -> GpuStoreAddress {
-        let address = self.get_next_address();
-
-        for _ in 0..count {
-            self.data.push(T::default());
-        }
-
-        address
-    }
-
-    pub fn get_next_address(&self) -> GpuStoreAddress {
-        GpuStoreAddress(self.data.len() as i32)
-    }
-
-    pub fn get(&mut self, address: GpuStoreAddress) -> &T {
-        &self.data[address.0 as usize]
-    }
-
-    pub fn get_mut(&mut self, address: GpuStoreAddress) -> &mut T {
-        &mut self.data[address.0 as usize]
-    }
-
-    pub fn get_slice_mut(&mut self,
-                         address: GpuStoreAddress,
-                         count: usize) -> &mut [T] {
-        let offset = address.0 as usize;
-        &mut self.data[offset..offset + count]
-    }
-
-    pub fn clear(&mut self) {
-        self.data.clear()
-    }
-
-    // TODO(gw): Implement incremental updates of
-    // GPU backed data, and support freelist for removing
-    // dynamic items.
-
-    /*
-    pub fn free(&mut self, address: GpuStoreAddress) {
-
-    }
-
-    pub fn update(&mut self, address: GpuStoreAddress, data: T) {
-
-    }*/
-}
--- a/gfx/webrender/src/internal_types.rs
+++ b/gfx/webrender/src/internal_types.rs
@@ -9,18 +9,18 @@ use profiler::BackendProfileCounters;
 use std::collections::{HashMap, HashSet};
 use std::f32;
 use std::hash::BuildHasherDefault;
 use std::{i32, usize};
 use std::path::PathBuf;
 use std::sync::Arc;
 use tiling;
 use renderer::BlendMode;
-use webrender_traits::{ClipId, ColorF, DeviceUintRect, Epoch, ExternalImageData, ExternalImageId};
-use webrender_traits::{DevicePoint, ImageData, ImageFormat, PipelineId};
+use api::{ClipId, ColorF, DeviceUintRect, Epoch, ExternalImageData, ExternalImageId};
+use api::{DevicePoint, ImageData, ImageFormat, PipelineId};
 
 // An ID for a texture that is owned by the
 // texture cache module. This can include atlases
 // or standalone textures allocated via the
 // texture cache (e.g. if an image is too large
 // to be added to an atlas). The texture cache
 // manages the allocation and freeing of these
 // IDs, and the rendering thread maintains a
@@ -54,17 +54,16 @@ pub const ORTHO_FAR_PLANE: f32 = 1000000
 
 #[derive(Debug, PartialEq, Eq)]
 pub enum TextureSampler {
     Color0,
     Color1,
     Color2,
     CacheA8,
     CacheRGBA8,
-    Data32,
     ResourceCache,
     Layers,
     RenderTasks,
     Dither,
 }
 
 impl TextureSampler {
     pub fn color(n: usize) -> TextureSampler {
@@ -166,38 +165,38 @@ pub struct DebugFontVertex {
     pub color: PackedColor,
     pub u: f32,
     pub v: f32,
 }
 
 impl DebugFontVertex {
     pub fn new(x: f32, y: f32, u: f32, v: f32, color: PackedColor) -> DebugFontVertex {
         DebugFontVertex {
-            x: x,
-            y: y,
-            color: color,
-            u: u,
-            v: v,
+            x,
+            y,
+            color,
+            u,
+            v,
         }
     }
 }
 
 #[repr(C)]
 pub struct DebugColorVertex {
     pub x: f32,
     pub y: f32,
     pub color: PackedColor,
 }
 
 impl DebugColorVertex {
     pub fn new(x: f32, y: f32, color: PackedColor) -> DebugColorVertex {
         DebugColorVertex {
-            x: x,
-            y: y,
-            color: color,
+            x,
+            y,
+            color,
         }
     }
 }
 
 #[derive(Copy, Clone, Debug, PartialEq)]
 pub enum RenderTargetMode {
     None,
     SimpleRenderTarget,
@@ -274,19 +273,19 @@ pub struct RendererFrame {
 }
 
 impl RendererFrame {
     pub fn new(pipeline_epoch_map: HashMap<PipelineId, Epoch, BuildHasherDefault<FnvHasher>>,
                layers_bouncing_back: HashSet<ClipId, BuildHasherDefault<FnvHasher>>,
                frame: Option<tiling::Frame>)
                -> RendererFrame {
         RendererFrame {
-            pipeline_epoch_map: pipeline_epoch_map,
-            layers_bouncing_back: layers_bouncing_back,
-            frame: frame,
+            pipeline_epoch_map,
+            layers_bouncing_back,
+            frame,
         }
     }
 }
 
 pub enum ResultMsg {
     RefreshShader(PathBuf),
     NewFrame(RendererFrame, TextureUpdateList, BackendProfileCounters),
 }
--- a/gfx/webrender/src/lib.rs
+++ b/gfx/webrender/src/lib.rs
@@ -28,17 +28,17 @@
 //! More information about [stacking contexts][stacking_contexts].
 //!
 //! `set_display_list()` also needs to be supplied with `BuiltDisplayList`s.
 //! These are obtained by finalizing a `DisplayListBuilder`. These are used to draw your geometry.
 //! But it doesn't only contain trivial geometry, it can also store another StackingContext, as
 //! they're nestable.
 //!
 //! [stacking_contexts]: https://developer.mozilla.org/en-US/docs/Web/CSS/CSS_Positioning/Understanding_z_index/The_stacking_context
-//! [newframe]: ../webrender_traits/struct.RenderApi.html#method.set_display_list
+//! [newframe]: ../webrender_api/struct.RenderApi.html#method.set_display_list
 //! [notifier]: renderer/struct.Renderer.html#method.set_render_notifier
 
 #[macro_use]
 extern crate lazy_static;
 #[macro_use]
 extern crate log;
 #[macro_use]
 extern crate bitflags;
@@ -54,17 +54,16 @@ mod debug_render;
 mod device;
 mod ellipse;
 mod frame;
 mod frame_builder;
 mod freelist;
 mod geometry;
 mod glyph_rasterizer;
 mod gpu_cache;
-mod gpu_store;
 mod internal_types;
 mod mask_cache;
 mod prim_store;
 mod print_tree;
 mod profiler;
 mod record;
 mod render_backend;
 mod render_task;
@@ -129,20 +128,22 @@ extern crate dwrote;
 extern crate app_units;
 extern crate bincode;
 extern crate euclid;
 extern crate fnv;
 extern crate gleam;
 extern crate num_traits;
 //extern crate notify;
 extern crate time;
-extern crate webrender_traits;
+pub extern crate webrender_api;
 #[cfg(feature = "webgl")]
 extern crate offscreen_gl_context;
 extern crate byteorder;
 extern crate rayon;
 extern crate plane_split;
 
 #[cfg(any(target_os="macos", target_os="windows"))]
 extern crate gamma_lut;
 
 pub use renderer::{ExternalImage, ExternalImageSource, ExternalImageHandler};
 pub use renderer::{GraphicsApi, GraphicsApiInfo, ReadPixelsFormat, Renderer, RendererOptions};
+
+pub use webrender_api as api;
--- a/gfx/webrender/src/mask_cache.rs
+++ b/gfx/webrender/src/mask_cache.rs
@@ -1,25 +1,69 @@
 /* This Source Code Form is subject to the terms of the Mozilla Public
  * License, v. 2.0. If a copy of the MPL was not distributed with this
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
+use api::{BorderRadius, ComplexClipRegion, DeviceIntRect, ImageMask, LayerPoint, LayerRect};
+use api::{LayerSize, LayerToWorldTransform, LocalClip};
 use border::BorderCornerClipSource;
-use gpu_store::GpuStoreAddress;
-use prim_store::{ClipData, GpuBlock32, ImageMaskData, PrimitiveStore};
-use prim_store::{CLIP_DATA_GPU_SIZE, MASK_DATA_GPU_SIZE};
-use renderer::VertexDataStore;
-use util::{ComplexClipRegionHelpers, MatrixHelpers, TransformedRect};
-use webrender_traits::{BorderRadius, BuiltDisplayList, ClipRegion, ComplexClipRegion, ImageMask};
-use webrender_traits::{DeviceIntRect, LayerToWorldTransform};
-use webrender_traits::{DeviceRect, LayerRect, LayerPoint, LayerSize};
+use gpu_cache::{GpuCache, GpuCacheHandle, ToGpuBlocks};
+use prim_store::{CLIP_DATA_GPU_BLOCKS, ClipData, ImageMaskData};
+use util::{ComplexClipRegionHelpers, TransformedRect};
 use std::ops::Not;
 
 const MAX_CLIP: f32 = 1000000.0;
 
+#[derive(Clone, Debug)]
+pub struct ClipRegion {
+    pub origin: LayerPoint,
+    pub main: LayerRect,
+    pub image_mask: Option<ImageMask>,
+    pub complex_clips: Vec<ComplexClipRegion>,
+}
+
+impl ClipRegion {
+    pub fn for_clip_node(rect: LayerRect,
+                         mut complex_clips: Vec<ComplexClipRegion>,
+                         mut image_mask: Option<ImageMask>)
+                         -> ClipRegion {
+        // All the coordinates we receive are relative to the stacking context, but we want
+        // to convert them to something relative to the origin of the clip.
+        let negative_origin = -rect.origin.to_vector();
+        if let Some(ref mut image_mask) = image_mask {
+            image_mask.rect = image_mask.rect.translate(&negative_origin);
+        }
+
+        for complex_clip in complex_clips.iter_mut() {
+            complex_clip.rect = complex_clip.rect.translate(&negative_origin);
+        }
+
+        ClipRegion {
+            origin: rect.origin,
+            main: LayerRect::new(LayerPoint::zero(), rect.size),
+            image_mask,
+            complex_clips,
+        }
+    }
+
+    pub fn for_local_clip(local_clip: &LocalClip) -> ClipRegion {
+        let complex_clips = match local_clip {
+            &LocalClip::Rect(_) => Vec::new(),
+            &LocalClip::RoundedRect(_, ref region) => vec![region.clone()],
+        };
+
+        ClipRegion {
+            origin: LayerPoint::zero(),
+            main: *local_clip.clip_rect(),
+            image_mask: None,
+            complex_clips,
+        }
+    }
+}
+
 #[repr(C)]
 #[derive(Copy, Clone, Debug, PartialEq)]
 pub enum ClipMode {
     Clip,           // Pixels inside the region are visible.
     ClipOut,        // Pixels outside the region are visible.
 }
 
 impl Not for ClipMode {
@@ -28,305 +72,301 @@ impl Not for ClipMode {
     fn not(self) -> ClipMode {
         match self {
             ClipMode::Clip => ClipMode::ClipOut,
             ClipMode::ClipOut => ClipMode::Clip
         }
     }
 }
 
-#[repr(C)]
-#[derive(Copy, Clone, Debug, PartialEq)]
-pub enum RegionMode {
-    IncludeRect,
-    ExcludeRect,
-}
-
 #[derive(Clone, Debug)]
 pub enum ClipSource {
     Complex(LayerRect, f32, ClipMode),
-    // The RegionMode here specifies whether to consider the rect
-    // from the clip region as part of the mask. This is true
-    // for clip/scroll nodes, but false for primitives, where
-    // the clip rect is handled in local space.
-    Region(ClipRegion, RegionMode),
-
-    // TODO(gw): This currently only handles dashed style
-    // clips, where the border style is dashed for both
-    // adjacent border edges. Expand to handle dotted style
-    // and different styles per edge.
+    Region(ClipRegion),
+    /// TODO(gw): This currently only handles dashed style
+    /// clips, where the border style is dashed for both
+    /// adjacent border edges. Expand to handle dotted style
+    /// and different styles per edge.
     BorderCorner(BorderCornerClipSource),
 }
 
 impl ClipSource {
     pub fn image_mask(&self) -> Option<ImageMask> {
         match *self {
             ClipSource::Complex(..) |
-            ClipSource::BorderCorner{..} => None,
-            ClipSource::Region(ref region, _) => region.image_mask,
+            ClipSource::BorderCorner(..) => None,
+            ClipSource::Region(ref region) => region.image_mask,
         }
     }
 }
 
-#[derive(Debug, Copy, Clone, Eq, PartialEq, Hash)]
+#[derive(Debug, Copy, Clone)]
 pub struct ClipAddressRange {
-    pub start: GpuStoreAddress,
+    pub location: GpuCacheHandle,
     item_count: usize,
 }
 
+impl ClipAddressRange {
+    fn new(count: usize) -> Self {
+        ClipAddressRange {
+            location: GpuCacheHandle::new(),
+            item_count: count,
+        }
+    }
+
+    pub fn get_count(&self) -> usize {
+        self.item_count
+    }
+
+    fn get_block_count(&self) -> Option<usize> {
+        if self.item_count != 0 {
+            Some(self.item_count * CLIP_DATA_GPU_BLOCKS)
+        } else {
+            None
+        }
+    }
+}
+
 /// Represents a local rect and a device space
-/// bounding rect that can be updated when the
-/// transform changes.
+/// rectangles that are either outside or inside bounds.
 #[derive(Clone, Debug, PartialEq)]
 pub struct Geometry {
     pub local_rect: LayerRect,
-    pub bounding_rect: DeviceIntRect,
+    pub device_rect: DeviceIntRect,
 }
 
-impl Geometry {
-    fn new(local_rect: LayerRect) -> Geometry {
+impl From<LayerRect> for Geometry {
+    fn from(local_rect: LayerRect) -> Self {
         Geometry {
-            local_rect: local_rect,
-            bounding_rect: DeviceIntRect::zero(),
+            local_rect,
+            device_rect: DeviceIntRect::zero(),
         }
     }
-
-    fn update(&mut self,
-              transform: &LayerToWorldTransform,
-              device_pixel_ratio: f32) {
-        let transformed = TransformedRect::new(&self.local_rect,
-                                               transform,
-                                               device_pixel_ratio);
-        self.bounding_rect = transformed.bounding_rect;
-    }
 }
 
 /// Depending on the complexity of the clip, we may either
 /// know the outer and/or inner rect, or neither or these.
 /// In the case of a clip-out, we currently set the mask
 /// bounds to be unknown. This is conservative, but ensures
 /// correctness. In the future we can make this a lot
 /// more clever with some proper region handling.
 #[derive(Clone, Debug, PartialEq)]
-pub enum MaskBounds {
-    /// We know both the outer and inner rect. This is the
-    /// fast path for, e.g. a simple rounded rect.
-    OuterInner(Geometry, Geometry),
-    /// We know the outer rect only.
-    Outer(Geometry),
-    /// We can't determine the bounds - draw mask over entire rect.
-    /// This is currently used for clip-out operations on
-    /// box shadows.
-    None,
+pub struct MaskBounds {
+    pub outer: Option<Geometry>,
+    pub inner: Option<Geometry>,
+}
+
+impl MaskBounds {
+    pub fn update(&mut self, transform: &LayerToWorldTransform, device_pixel_ratio: f32) {
+        if let Some(ref mut outer) = self.outer {
+            let transformed = TransformedRect::new(&outer.local_rect,
+                                                   transform,
+                                                   device_pixel_ratio);
+            outer.device_rect = transformed.bounding_rect;
+        }
+        if let Some(ref mut inner) = self.inner {
+            let transformed = TransformedRect::new(&inner.local_rect,
+                                                   transform,
+                                                   device_pixel_ratio);
+            inner.device_rect = transformed.inner_rect;
+        }
+    }
 }
 
 #[derive(Clone, Debug)]
 pub struct MaskCacheInfo {
+    /// Clip items that are always applied
     pub complex_clip_range: ClipAddressRange,
-    pub effective_complex_clip_count: usize,
-    pub image: Option<(ImageMask, GpuStoreAddress)>,
-    pub border_corners: Vec<(BorderCornerClipSource, GpuStoreAddress)>,
-    pub bounds: Option<MaskBounds>,
-    pub is_aligned: bool,
+    /// Clip items that are only applied if the clip space is transformed from
+    /// the local space of target primitive/layer.
+    pub layer_clip_range: ClipAddressRange,
+    pub image: Option<(ImageMask, GpuCacheHandle)>,
+    pub border_corners: Vec<(BorderCornerClipSource, GpuCacheHandle)>,
+    pub bounds: MaskBounds,
 }
 
 impl MaskCacheInfo {
     /// Create a new mask cache info. It allocates the GPU store data but leaves
-    /// it unitialized for the following `update()` call to deal with.
-    pub fn new(clips: &[ClipSource],
-               clip_store: &mut VertexDataStore<GpuBlock32>)
-               -> Option<MaskCacheInfo> {
-        if clips.is_empty() {
-            return None;
-        }
-
+    /// it uninitialized for the following `update()` call to deal with.
+    pub fn new(clips: &[ClipSource]) -> MaskCacheInfo {
         let mut image = None;
         let mut border_corners = Vec::new();
         let mut complex_clip_count = 0;
+        let mut layer_clip_count = 0;
 
         // Work out how much clip data space we need to allocate
         // and if we have an image mask.
         for clip in clips {
             match *clip {
                 ClipSource::Complex(..) => {
                     complex_clip_count += 1;
                 }
-                ClipSource::Region(ref region, region_mode) => {
+                ClipSource::Region(ref region) => {
                     if let Some(info) = region.image_mask {
                         debug_assert!(image.is_none());     // TODO(gw): Support >1 image mask!
-                        image = Some((info, clip_store.alloc(MASK_DATA_GPU_SIZE)));
+                        image = Some((info, GpuCacheHandle::new()));
                     }
-                    complex_clip_count += region.complex_clip_count;
-                    if region_mode == RegionMode::IncludeRect {
-                        complex_clip_count += 1;
-                    }
+                    complex_clip_count += region.complex_clips.len();
+                    layer_clip_count += 1;
                 }
                 ClipSource::BorderCorner(ref source) => {
-                    // One block for the corner header, plus one
-                    // block per dash to clip out.
-                    let gpu_address = clip_store.alloc(1 + source.max_clip_count);
-                    border_corners.push((source.clone(), gpu_address));
+                    border_corners.push((source.clone(), GpuCacheHandle::new()));
                 }
             }
         }
 
-        let complex_clip_range = ClipAddressRange {
-            start: if complex_clip_count > 0 {
-                clip_store.alloc(CLIP_DATA_GPU_SIZE * complex_clip_count)
-            } else {
-                GpuStoreAddress(0)
+        MaskCacheInfo {
+            complex_clip_range: ClipAddressRange::new(complex_clip_count),
+            layer_clip_range: ClipAddressRange::new(layer_clip_count),
+            image,
+            border_corners,
+            bounds: MaskBounds {
+                inner: None,
+                outer: None,
             },
-            item_count: complex_clip_count,
-        };
-
-        Some(MaskCacheInfo {
-            complex_clip_range: complex_clip_range,
-            effective_complex_clip_count: complex_clip_range.item_count,
-            image: image,
-            border_corners: border_corners,
-            bounds: None,
-            is_aligned: true,
-        })
+        }
     }
 
     pub fn update(&mut self,
                   sources: &[ClipSource],
                   transform: &LayerToWorldTransform,
-                  clip_store: &mut VertexDataStore<GpuBlock32>,
-                  device_pixel_ratio: f32,
-                  display_list: &BuiltDisplayList) {
-        let is_aligned = transform.preserves_2d_axis_alignment();
+                  gpu_cache: &mut GpuCache,
+                  device_pixel_ratio: f32)
+                  -> &MaskBounds {
 
-        // If we haven't cached this info, or if the transform type has changed
-        // we need to re-calculate the number of clips.
-        if self.bounds.is_none() || self.is_aligned != is_aligned {
+        // Step[1] - compute the local bounds
+        //TODO: move to initialization stage?
+        if self.bounds.inner.is_none() {
             let mut local_rect = Some(LayerRect::new(LayerPoint::new(-MAX_CLIP, -MAX_CLIP),
                                                      LayerSize::new(2.0 * MAX_CLIP, 2.0 * MAX_CLIP)));
             let mut local_inner: Option<LayerRect> = None;
             let mut has_clip_out = false;
-            let mut has_border_clip = false;
-
-            self.effective_complex_clip_count = 0;
-            self.is_aligned = is_aligned;
+            let has_border_clip = !self.border_corners.is_empty();
 
             for source in sources {
                 match *source {
                     ClipSource::Complex(rect, radius, mode) => {
                         // Once we encounter a clip-out, we just assume the worst
                         // case clip mask size, for now.
                         if mode == ClipMode::ClipOut {
                             has_clip_out = true;
+                            break;
                         }
-                        debug_assert!(self.effective_complex_clip_count < self.complex_clip_range.item_count);
-                        let address = self.complex_clip_range.start + self.effective_complex_clip_count * CLIP_DATA_GPU_SIZE;
-                        self.effective_complex_clip_count += 1;
-
-                        let slice = clip_store.get_slice_mut(address, CLIP_DATA_GPU_SIZE);
-                        let data = ClipData::uniform(rect, radius, mode);
-                        PrimitiveStore::populate_clip_data(slice, data);
                         local_rect = local_rect.and_then(|r| r.intersection(&rect));
                         local_inner = ComplexClipRegion::new(rect, BorderRadius::uniform(radius))
                                                         .get_inner_rect_safe();
                     }
-                    ClipSource::Region(ref region, region_mode) => {
+                    ClipSource::Region(ref region) => {
                         local_rect = local_rect.and_then(|r| r.intersection(&region.main));
                         local_inner = match region.image_mask {
-                            Some(ref mask) if !mask.repeat => {
-                                local_rect = local_rect.and_then(|r| r.intersection(&mask.rect));
+                            Some(ref mask) => {
+                                if !mask.repeat {
+                                    local_rect = local_rect.and_then(|r| r.intersection(&mask.rect));
+                                }
                                 None
                             },
-                            Some(_) => None,
                             None => local_rect,
                         };
 
-                        let clips = display_list.get(region.complex_clips);
-                        if !self.is_aligned && region_mode == RegionMode::IncludeRect {
-                            // we have an extra clip rect coming from the transformed layer
-                            debug_assert!(self.effective_complex_clip_count < self.complex_clip_range.item_count);
-                            let address = self.complex_clip_range.start + self.effective_complex_clip_count * CLIP_DATA_GPU_SIZE;
-                            self.effective_complex_clip_count += 1;
-
-                            let slice = clip_store.get_slice_mut(address, CLIP_DATA_GPU_SIZE);
-                            PrimitiveStore::populate_clip_data(slice, ClipData::uniform(region.main, 0.0, ClipMode::Clip));
-                        }
-
-                        debug_assert!(self.effective_complex_clip_count + clips.len() <= self.complex_clip_range.item_count);
-                        let address = self.complex_clip_range.start + self.effective_complex_clip_count * CLIP_DATA_GPU_SIZE;
-                        self.effective_complex_clip_count += clips.len();
-
-                        let slice = clip_store.get_slice_mut(address, CLIP_DATA_GPU_SIZE * clips.len());
-                        for (clip, chunk) in clips.zip(slice.chunks_mut(CLIP_DATA_GPU_SIZE)) {
-                            let data = ClipData::from_clip_region(&clip);
-                            PrimitiveStore::populate_clip_data(chunk, data);
+                        for clip in &region.complex_clips {
                             local_rect = local_rect.and_then(|r| r.intersection(&clip.rect));
                             local_inner = local_inner.and_then(|r| clip.get_inner_rect_safe()
                                                                        .and_then(|ref inner| r.intersection(inner)));
                         }
                     }
                     ClipSource::BorderCorner{..} => {}
                 }
             }
 
-            for &mut (ref mut source, gpu_address) in &mut self.border_corners {
-                has_border_clip = true;
-                let slice = clip_store.get_slice_mut(gpu_address,
-                                                     1 + source.max_clip_count);
-                source.populate_gpu_data(slice);
-            }
-
-            if let Some((ref mask, gpu_address)) = self.image {
-                let mask_data = clip_store.get_slice_mut(gpu_address, MASK_DATA_GPU_SIZE);
-                mask_data[0] = GpuBlock32::from(ImageMaskData {
-                    padding: DeviceRect::zero(),
-                    local_rect: mask.rect,
-                });
-            }
-
             // Work out the type of mask geometry we have, based on the
             // list of clip sources above.
-            if has_clip_out || has_border_clip {
+            self.bounds = if has_clip_out || has_border_clip {
                 // For clip-out, the mask rect is not known.
-                self.bounds = Some(MaskBounds::None);
+                MaskBounds {
+                    outer: None,
+                    inner: Some(LayerRect::zero().into()),
+                }
             } else {
                 // TODO(gw): local inner is only valid if there's a single clip (for now).
                 // This can be improved in the future, with some proper
                 // rectangle region handling.
                 if sources.len() > 1 {
                     local_inner = None;
                 }
 
-                let local_rect = local_rect.unwrap_or(LayerRect::zero());
+                MaskBounds {
+                    outer: Some(local_rect.unwrap_or(LayerRect::zero()).into()),
+                    inner: Some(local_inner.unwrap_or(LayerRect::zero()).into()),
+                }
+            };
+        }
+
+        // Step[2] - update GPU cache data
 
-                self.bounds = match local_inner {
-                    Some(local_inner) => {
-                        Some(MaskBounds::OuterInner(Geometry::new(local_rect),
-                                                    Geometry::new(local_inner)))
+        if let Some(block_count) = self.complex_clip_range.get_block_count() {
+            if let Some(mut request) = gpu_cache.request(&mut self.complex_clip_range.location) {
+                for source in sources {
+                    match *source {
+                        ClipSource::Complex(rect, radius, mode) => {
+                            let data = ClipData::uniform(rect, radius, mode);
+                            data.write(&mut request);
+                        }
+                        ClipSource::Region(ref region) => {
+                            for clip in &region.complex_clips {
+                                let data = ClipData::from_clip_region(&clip);
+                                data.write(&mut request);
+                            }
+                        }
+                        ClipSource::BorderCorner{..} => {}
                     }
-                    None => {
-                        Some(MaskBounds::Outer(Geometry::new(local_rect)))
-                    }
-                };
+                }
+                assert_eq!(request.close(), block_count);
             }
         }
 
-        // Update the device space bounding rects of the mask
-        // geometry.
-        match self.bounds.as_mut().unwrap() {
-            &mut MaskBounds::None => {}
-            &mut MaskBounds::Outer(ref mut outer) => {
-                outer.update(transform, device_pixel_ratio);
+        if let Some(block_count) = self.layer_clip_range.get_block_count() {
+            if let Some(mut request) = gpu_cache.request(&mut self.layer_clip_range.location) {
+                for source in sources {
+                    if let ClipSource::Region(ref region) = *source {
+                        let data = ClipData::uniform(region.main, 0.0, ClipMode::Clip);
+                        data.write(&mut request);
+                    }
+                }
+                assert_eq!(request.close(), block_count);
             }
-            &mut MaskBounds::OuterInner(ref mut outer, ref mut inner) => {
-                outer.update(transform, device_pixel_ratio);
-                inner.update(transform, device_pixel_ratio);
+        }
+
+        for &mut (ref mut border_source, ref mut gpu_location) in &mut self.border_corners {
+            if let Some(request) = gpu_cache.request(gpu_location) {
+                border_source.write(request);
             }
         }
+
+        if let Some((ref mask, ref mut gpu_location)) = self.image {
+            if let Some(request) = gpu_cache.request(gpu_location) {
+                let data = ImageMaskData {
+                    local_rect: mask.rect,
+                };
+                data.write_gpu_blocks(request);
+            }
+        }
+
+        // Step[3] - update the screen bounds
+        self.bounds.update(transform, device_pixel_ratio);
+        &self.bounds
     }
 
-    /// Check if this `MaskCacheInfo` actually carries any masks. `effective_complex_clip_count`
-    /// can change during the `update` call depending on the transformation, so the mask may
-    /// appear to be empty.
+    /// Check if this `MaskCacheInfo` actually carries any masks.
     pub fn is_masking(&self) -> bool {
         self.image.is_some() ||
-        self.effective_complex_clip_count != 0 ||
+        self.complex_clip_range.item_count != 0 ||
+        self.layer_clip_range.item_count != 0 ||
         !self.border_corners.is_empty()
     }
+
+    /// Return a clone of this object without any layer-aligned clip items
+    pub fn strip_aligned(&self) -> Self {
+        MaskCacheInfo {
+            layer_clip_range: ClipAddressRange::new(0),
+            .. self.clone()
+        }
+    }
 }
--- a/gfx/webrender/src/platform/macos/font.rs
+++ b/gfx/webrender/src/platform/macos/font.rs
@@ -10,19 +10,19 @@ use core_graphics::context::{CGContext, 
 use core_graphics::data_provider::CGDataProvider;
 use core_graphics::font::{CGFont, CGGlyph};
 use core_graphics::geometry::{CGPoint, CGSize, CGRect};
 use core_text::font::CTFont;
 use core_text::font_descriptor::kCTFontDefaultOrientation;
 use core_text;
 use std::collections::HashMap;
 use std::collections::hash_map::Entry;
-use webrender_traits::{ColorU, FontKey, FontRenderMode, GlyphDimensions};
-use webrender_traits::{GlyphKey, GlyphOptions, SubpixelPoint};
-use webrender_traits::NativeFontHandle;
+use api::{ColorU, FontKey, FontRenderMode, GlyphDimensions};
+use api::{GlyphKey, GlyphOptions, SubpixelPoint};
+use api::NativeFontHandle;
 use gamma_lut::{GammaLut, Color as ColorLut};
 
 pub struct FontContext {
     cg_fonts: HashMap<FontKey, CGFont>,
     ct_fonts: HashMap<(FontKey, Au), CTFont>,
     gamma_lut: GammaLut,
 }
 
--- a/gfx/webrender/src/platform/unix/font.rs
+++ b/gfx/webrender/src/platform/unix/font.rs
@@ -1,16 +1,16 @@
 /* This Source Code Form is subject to the terms of the Mozilla Public
  * License, v. 2.0. If a copy of the MPL was not distributed with this
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 use app_units::Au;
-use webrender_traits::{FontKey, FontRenderMode, GlyphDimensions};
-use webrender_traits::{NativeFontHandle, GlyphOptions};
-use webrender_traits::{GlyphKey};
+use api::{FontKey, FontRenderMode, GlyphDimensions};
+use api::{NativeFontHandle, GlyphOptions};
+use api::{GlyphKey};
 
 use freetype::freetype::{FT_Render_Mode, FT_Pixel_Mode};
 use freetype::freetype::{FT_Done_FreeType, FT_Library_SetLcdFilter};
 use freetype::freetype::{FT_Library, FT_Set_Char_Size};
 use freetype::freetype::{FT_Face, FT_Long, FT_UInt, FT_F26Dot6};
 use freetype::freetype::{FT_Init_FreeType, FT_Load_Glyph, FT_Render_Glyph};
 use freetype::freetype::{FT_New_Memory_Face, FT_GlyphSlot, FT_LcdFilter};
 use freetype::freetype::{FT_Done_Face, FT_Error};
@@ -60,17 +60,17 @@ impl FontContext {
             // TODO(gw): Check result of this to determine if freetype build supports subpixel.
             let result = FT_Library_SetLcdFilter(lib, FT_LcdFilter::FT_LCD_FILTER_DEFAULT);
             if !result.succeeded() {
                 println!("WARN: Initializing a FreeType library build without subpixel AA enabled!");
             }
         }
 
         FontContext {
-            lib: lib,
+            lib,
             faces: HashMap::new(),
         }
     }
 
     pub fn has_font(&self, font_key: &FontKey) -> bool {
         self.faces.contains_key(font_key)
     }
 
@@ -81,17 +81,17 @@ impl FontContext {
                 FT_New_Memory_Face(self.lib,
                                    bytes.as_ptr(),
                                    bytes.len() as FT_Long,
                                    index as FT_Long,
                                    &mut face)
             };
             if result.succeeded() && !face.is_null() {
                 self.faces.insert(*font_key, Face {
-                    face: face,
+                    face,
                     //_bytes: bytes
                 });
             } else {
                 println!("WARN: webrender failed to load font {:?}", font_key);
             }
         }
     }
 
--- a/gfx/webrender/src/platform/windows/font.rs
+++ b/gfx/webrender/src/platform/windows/font.rs
@@ -1,15 +1,15 @@
 /* This Source Code Form is subject to the terms of the Mozilla Public
  * License, v. 2.0. If a copy of the MPL was not distributed with this
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 use std::collections::HashMap;
-use webrender_traits::{FontKey, FontRenderMode, GlyphDimensions};
-use webrender_traits::{GlyphKey, GlyphOptions};
+use api::{FontKey, FontRenderMode, GlyphDimensions};
+use api::{GlyphKey, GlyphOptions};
 use gamma_lut::{GammaLut, Color as ColorLut};
 
 use dwrote;
 
 lazy_static! {
     static ref DEFAULT_FONT_DESCRIPTOR: dwrote::FontDescriptor = dwrote::FontDescriptor {
         family_name: "Arial".to_owned(),
         weight: dwrote::FontWeight::Regular,
@@ -98,18 +98,18 @@ fn get_glyph_dimensions_with_analysis(an
     // Such as for spaces
     if width == 0 || height == 0 {
         return None
     }
 
     Some(GlyphDimensions {
         left: bounds.left,
         top: -bounds.top,
-        width: width,
-        height: height,
+        width,
+        height,
     })
 }
 
 impl FontContext {
     pub fn new() -> FontContext {
         // These are the default values we use in Gecko.
         // We use a gamma value of 2.3 for gdi fonts
         // TODO: Fetch this data from Gecko itself.
--- a/gfx/webrender/src/prim_store.rs
+++ b/gfx/webrender/src/prim_store.rs
@@ -1,36 +1,59 @@
 /* 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::{BuiltDisplayList, ColorF, ComplexClipRegion, DeviceIntRect, DeviceIntSize, DevicePoint};
+use api::{ExtendMode, FontKey, FontRenderMode, GlyphInstance, GlyphOptions, GradientStop};
+use api::{ImageKey, ImageRendering, ItemRange, LayerPoint, LayerRect, LayerSize};
+use api::{LayerToWorldTransform, TileOffset, WebGLContextId, YuvColorSpace, YuvFormat};
+use api::device_length;
 use app_units::Au;
-use border::{BorderCornerClipData, BorderCornerDashClipData, BorderCornerDotClipData};
 use border::BorderCornerInstance;
 use euclid::{Size2D};
 use gpu_cache::{GpuCacheAddress, GpuBlockData, GpuCache, GpuCacheHandle, GpuDataRequest, ToGpuBlocks};
-use gpu_store::GpuStoreAddress;
-use mask_cache::{ClipMode, ClipSource, MaskCacheInfo};
-use renderer::{VertexDataStore, MAX_VERTEX_TEXTURE_WIDTH};
+use mask_cache::{ClipMode, ClipRegion, ClipSource, MaskCacheInfo};
+use renderer::MAX_VERTEX_TEXTURE_WIDTH;
 use render_task::{RenderTask, RenderTaskLocation};
 use resource_cache::{ImageProperties, ResourceCache};
-use std::mem;
-use std::usize;
+use std::{mem, usize};
 use util::{TransformedRect, recycle_vec};
-use webrender_traits::{BuiltDisplayList, ColorF, ImageKey, ImageRendering, YuvColorSpace};
-use webrender_traits::{YuvFormat, ClipRegion, ComplexClipRegion, ItemRange};
-use webrender_traits::{FontKey, FontRenderMode, WebGLContextId};
-use webrender_traits::{device_length, DeviceIntRect, DeviceIntSize};
-use webrender_traits::{DeviceRect, DevicePoint};
-use webrender_traits::{LayerRect, LayerSize, LayerPoint};
-use webrender_traits::{LayerToWorldTransform, GlyphInstance, GlyphOptions};
-use webrender_traits::{ExtendMode, GradientStop, TileOffset};
+
+
+pub const CLIP_DATA_GPU_BLOCKS: usize = 10;
+
+#[derive(Debug, Copy, Clone)]
+pub struct PrimitiveOpacity {
+    pub is_opaque: bool,
+}
+
+impl PrimitiveOpacity {
+    pub fn opaque() -> PrimitiveOpacity {
+        PrimitiveOpacity {
+            is_opaque: true,
+        }
+    }
 
-pub const CLIP_DATA_GPU_SIZE: usize = 5;
-pub const MASK_DATA_GPU_SIZE: usize = 1;
+    pub fn translucent() -> PrimitiveOpacity {
+        PrimitiveOpacity {
+            is_opaque: false,
+        }
+    }
+
+    pub fn from_alpha(alpha: f32) -> PrimitiveOpacity {
+        PrimitiveOpacity {
+            is_opaque: alpha == 1.0,
+        }
+    }
+
+    pub fn accumulate(&mut self, alpha: f32) {
+        self.is_opaque = self.is_opaque && alpha == 1.0;
+    }
+}
 
 /// Stores two coordinates in texel space. The coordinates
 /// are stored in texel coordinates because the texture atlas
 /// may grow. Storing them as texel coords and normalizing
 /// the UVs in the vertex shader means nothing needs to be
 /// updated on the CPU when the texture size changes.
 #[derive(Copy, Clone, Debug)]
 pub struct TexelRect {
@@ -109,17 +132,17 @@ impl GpuCacheHandle {
         //           to use 2x u16 for the vertex attribute instead of an i32.
         address.v as i32 * MAX_VERTEX_TEXTURE_WIDTH as i32 + address.u as i32
     }
 }
 
 // TODO(gw): Pack the fields here better!
 #[derive(Debug)]
 pub struct PrimitiveMetadata {
-    pub is_opaque: bool,
+    pub opacity: PrimitiveOpacity,
     pub clips: Vec<ClipSource>,
     pub clip_cache_info: Option<MaskCacheInfo>,
     pub prim_kind: PrimitiveKind,
     pub cpu_prim_index: SpecificPrimitiveIndex,
     pub gpu_location: GpuCacheHandle,
     // An optional render task that is a dependency of
     // drawing this primitive. For instance, box shadows
     // use this to draw a portion of the box shadow to
@@ -147,17 +170,17 @@ impl PrimitiveMetadata {
 #[derive(Debug, Clone)]
 #[repr(C)]
 pub struct RectanglePrimitive {
     pub color: ColorF,
 }
 
 impl ToGpuBlocks for RectanglePrimitive {
     fn write_gpu_blocks(&self, mut request: GpuDataRequest) {
-        request.push(self.color.into());
+        request.push(self.color);
     }
 }
 
 #[derive(Debug)]
 pub enum ImagePrimitiveKind {
     Image(ImageKey, ImageRendering, Option<TileOffset>, LayerSize),
     WebGL(WebGLContextId),
 }
@@ -224,49 +247,53 @@ pub struct BoxShadowPrimitiveCpu {
     pub edge_size: f32,
     pub blur_radius: f32,
     pub inverted: f32,
     pub rects: Vec<LayerRect>,
 }
 
 impl ToGpuBlocks for BoxShadowPrimitiveCpu {
     fn write_gpu_blocks(&self, mut request: GpuDataRequest) {
-        request.push(self.src_rect.into());
-        request.push(self.bs_rect.into());
-        request.push(self.color.into());
+        request.push(self.src_rect);
+        request.push(self.bs_rect);
+        request.push(self.color);
         request.push([self.border_radius,
                       self.edge_size,
                       self.blur_radius,
-                      self.inverted].into());
+                      self.inverted]);
         for &rect in &self.rects {
-            request.push(rect.into());
+            request.push(rect);
         }
     }
 }
 
 #[derive(Debug)]
 pub struct GradientPrimitiveCpu {
     pub stops_range: ItemRange<GradientStop>,
     pub stops_count: usize,
     pub extend_mode: ExtendMode,
     pub reverse_stops: bool,
     pub gpu_blocks: [GpuBlockData; 3],
 }
 
 impl GradientPrimitiveCpu {
     fn build_gpu_blocks_for_aligned(&self,
                                     display_list: &BuiltDisplayList,
-                                    mut request: GpuDataRequest) {
+                                    mut request: GpuDataRequest) -> PrimitiveOpacity {
+        let mut opacity = PrimitiveOpacity::opaque();
         request.extend_from_slice(&self.gpu_blocks);
         let src_stops = display_list.get(self.stops_range);
 
         for src in src_stops {
-            request.push(src.color.premultiplied().into());
-            request.push([src.offset, 0.0, 0.0, 0.0].into());
+            request.push(src.color.premultiplied());
+            request.push([src.offset, 0.0, 0.0, 0.0]);
+            opacity.accumulate(src.color.a);
         }
+
+        opacity
     }
 
     fn build_gpu_blocks_for_angle_radial(&self,
                                          display_list: &BuiltDisplayList,
                                          mut request: GpuDataRequest) {
         request.extend_from_slice(&self.gpu_blocks);
 
         let gradient_builder = GradientGpuBlockBuilder::new(self.stops_range,
@@ -302,18 +329,18 @@ struct GradientGpuBlockBuilder<'a> {
     stops_range: ItemRange<GradientStop>,
     display_list: &'a BuiltDisplayList,
 }
 
 impl<'a> GradientGpuBlockBuilder<'a> {
     fn new(stops_range: ItemRange<GradientStop>,
            display_list: &'a BuiltDisplayList) -> GradientGpuBlockBuilder<'a> {
         GradientGpuBlockBuilder {
-            stops_range: stops_range,
-            display_list: display_list,
+            stops_range,
+            display_list,
         }
     }
 
     /// Generate a color ramp filling the indices in [start_idx, end_idx) and interpolating
     /// from start_color to end_color.
     fn fill_colors(&self,
                    start_idx: usize,
                    end_idx: usize,
@@ -421,27 +448,26 @@ impl<'a> GradientGpuBlockBuilder<'a> {
             }
             debug_assert_eq!(cur_idx, GRADIENT_DATA_TABLE_END);
 
             // Fill in the last entry with the last color stop
             self.fill_colors(GRADIENT_DATA_LAST_STOP, GRADIENT_DATA_LAST_STOP + 1, &cur_color, &cur_color, &mut entries);
         }
 
         for entry in entries.iter() {
-            request.push(entry.start_color.into());
-            request.push(entry.end_color.into());
+            request.push(entry.start_color);
+            request.push(entry.end_color);
         }
     }
 }
 
 #[derive(Debug)]
 pub struct RadialGradientPrimitiveCpu {
     pub stops_range: ItemRange<GradientStop>,
     pub extend_mode: ExtendMode,
-    pub gpu_data_address: GpuStoreAddress,
     pub gpu_data_count: i32,
     pub gpu_blocks: [GpuBlockData; 3],
 }
 
 impl RadialGradientPrimitiveCpu {
     fn build_gpu_blocks_for_angle_radial(&self,
                                          display_list: &BuiltDisplayList,
                                          mut request: GpuDataRequest) {
@@ -464,92 +490,108 @@ pub struct TextRunPrimitiveCpu {
     pub glyph_instances: Vec<GlyphInstance>,
     pub color: ColorF,
     pub render_mode: FontRenderMode,
     pub glyph_options: Option<GlyphOptions>,
 }
 
 impl ToGpuBlocks for TextRunPrimitiveCpu {
     fn write_gpu_blocks(&self, mut request: GpuDataRequest) {
-        request.push(self.color.into());
+        request.push(self.color);
 
         // Two glyphs are packed per GPU block.
         for glyph_chunk in self.glyph_instances.chunks(2) {
             // In the case of an odd number of glyphs, the
             // last glyph will get duplicated in the final
             // GPU block.
             let first_glyph = glyph_chunk.first().unwrap();
             let second_glyph = glyph_chunk.last().unwrap();
             request.push([first_glyph.point.x,
                           first_glyph.point.y,
                           second_glyph.point.x,
-                          second_glyph.point.y].into());
+                          second_glyph.point.y]);
         }
     }
 }
 
 #[derive(Debug, Clone)]
 #[repr(C)]
 struct GlyphPrimitive {
     offset: LayerPoint,
     padding: LayerPoint,
 }
 
 #[derive(Debug, Clone)]
 #[repr(C)]
 struct ClipRect {
     rect: LayerRect,
     mode: f32,
-    padding: [f32; 3],
 }
 
 #[derive(Debug, Clone)]
 #[repr(C)]
 struct ClipCorner {
     rect: LayerRect,
     outer_radius_x: f32,
     outer_radius_y: f32,
     inner_radius_x: f32,
     inner_radius_y: f32,
 }
 
+impl ToGpuBlocks for ClipCorner {
+    fn write_gpu_blocks(&self, mut request: GpuDataRequest) {
+        self.write(&mut request)
+    }
+}
+
 impl ClipCorner {
+    fn write(&self, request: &mut GpuDataRequest) {
+        request.push(self.rect);
+        request.push([self.outer_radius_x, self.outer_radius_y,
+                      self.inner_radius_x, self.inner_radius_y,
+                     ]);
+    }
+
     fn uniform(rect: LayerRect, outer_radius: f32, inner_radius: f32) -> ClipCorner {
         ClipCorner {
-            rect: rect,
+            rect,
             outer_radius_x: outer_radius,
             outer_radius_y: outer_radius,
             inner_radius_x: inner_radius,
             inner_radius_y: inner_radius,
         }
     }
 }
 
 #[derive(Debug, Clone)]
 #[repr(C)]
 pub struct ImageMaskData {
     pub local_rect: LayerRect,
-    pub padding: DeviceRect,
+}
+
+impl ToGpuBlocks for ImageMaskData {
+    fn write_gpu_blocks(&self, mut request: GpuDataRequest) {
+        request.push(self.local_rect);
+    }
 }
 
 #[derive(Debug, Clone)]
 pub struct ClipData {
     rect: ClipRect,
     top_left: ClipCorner,
     top_right: ClipCorner,
     bottom_left: ClipCorner,
     bottom_right: ClipCorner,
 }
 
 impl ClipData {
     pub fn from_clip_region(clip: &ComplexClipRegion) -> ClipData {
         ClipData {
             rect: ClipRect {
                 rect: clip.rect,
-                padding: [0.0; 3],
                 // TODO(gw): Support other clip modes for regions?
                 mode: ClipMode::Clip as u32 as f32,
             },
             top_left: ClipCorner {
                 rect: LayerRect::new(
                     LayerPoint::new(clip.rect.origin.x, clip.rect.origin.y),
                     LayerSize::new(clip.radii.top_left.width, clip.radii.top_left.height)),
                 outer_radius_x: clip.radii.top_left.width,
@@ -586,18 +628,17 @@ impl ClipData {
                 inner_radius_y: 0.0,
             },
         }
     }
 
     pub fn uniform(rect: LayerRect, radius: f32, mode: ClipMode) -> ClipData {
         ClipData {
             rect: ClipRect {
-                rect: rect,
-                padding: [0.0; 3],
+                rect,
                 mode: mode as u32 as f32,
             },
             top_left: ClipCorner::uniform(
                 LayerRect::new(
                     LayerPoint::new(rect.origin.x, rect.origin.y),
                     LayerSize::new(radius, radius)),
                 radius, 0.0),
             top_right: ClipCorner::uniform(
@@ -612,16 +653,24 @@ impl ClipData {
                 radius, 0.0),
             bottom_right: ClipCorner::uniform(
                 LayerRect::new(
                     LayerPoint::new(rect.origin.x + rect.size.width - radius, rect.origin.y + rect.size.height - radius),
                     LayerSize::new(radius, radius)),
                 radius, 0.0),
         }
     }
+
+    pub fn write(&self, request: &mut GpuDataRequest) {
+        request.push(self.rect.rect);
+        request.push([self.rect.mode, 0.0, 0.0, 0.0]);
+        for corner in &[&self.top_left, &self.top_right, &self.bottom_left, &self.bottom_right] {
+            corner.write(request);
+        }
+    }
 }
 
 #[derive(Debug)]
 pub enum PrimitiveContainer {
     Rectangle(RectanglePrimitive),
     TextRun(TextRunPrimitiveCpu),
     Image(ImagePrimitiveCpu),
     YuvImage(YuvImagePrimitiveCpu),
@@ -639,201 +688,185 @@ pub struct PrimitiveStore {
     pub cpu_text_runs: Vec<TextRunPrimitiveCpu>,
     pub cpu_images: Vec<ImagePrimitiveCpu>,
     pub cpu_yuv_images: Vec<YuvImagePrimitiveCpu>,
     pub cpu_gradients: Vec<GradientPrimitiveCpu>,
     pub cpu_radial_gradients: Vec<RadialGradientPrimitiveCpu>,
     pub cpu_metadata: Vec<PrimitiveMetadata>,
     pub cpu_borders: Vec<BorderPrimitiveCpu>,
     pub cpu_box_shadows: Vec<BoxShadowPrimitiveCpu>,
-
-    /// Gets uploaded directly to GPU via vertex texture.
-    pub gpu_data32: VertexDataStore<GpuBlock32>,
 }
 
 impl PrimitiveStore {
     pub fn new() -> PrimitiveStore {
         PrimitiveStore {
             cpu_metadata: Vec::new(),
             cpu_rectangles: Vec::new(),
             cpu_bounding_rects: Vec::new(),
             cpu_text_runs: Vec::new(),
             cpu_images: Vec::new(),
             cpu_yuv_images: Vec::new(),
             cpu_gradients: Vec::new(),
             cpu_radial_gradients: Vec::new(),
             cpu_borders: Vec::new(),
             cpu_box_shadows: Vec::new(),
-            gpu_data32: VertexDataStore::new(),
         }
     }
 
     pub fn recycle(self) -> Self {
         PrimitiveStore {
             cpu_metadata: recycle_vec(self.cpu_metadata),
             cpu_rectangles: recycle_vec(self.cpu_rectangles),
             cpu_bounding_rects: recycle_vec(self.cpu_bounding_rects),
             cpu_text_runs: recycle_vec(self.cpu_text_runs),
             cpu_images: recycle_vec(self.cpu_images),
             cpu_yuv_images: recycle_vec(self.cpu_yuv_images),
             cpu_gradients: recycle_vec(self.cpu_gradients),
             cpu_radial_gradients: recycle_vec(self.cpu_radial_gradients),
             cpu_borders: recycle_vec(self.cpu_borders),
             cpu_box_shadows: recycle_vec(self.cpu_box_shadows),
-            gpu_data32: self.gpu_data32.recycle(),
         }
     }
 
-    pub fn populate_clip_data(data: &mut [GpuBlock32], clip: ClipData) {
-        data[0] = GpuBlock32::from(clip.rect);
-        data[1] = GpuBlock32::from(clip.top_left);
-        data[2] = GpuBlock32::from(clip.top_right);
-        data[3] = GpuBlock32::from(clip.bottom_left);
-        data[4] = GpuBlock32::from(clip.bottom_right);
-    }
-
     pub fn add_primitive(&mut self,
                          local_rect: &LayerRect,
                          local_clip_rect: &LayerRect,
                          clips: Vec<ClipSource>,
                          clip_info: Option<MaskCacheInfo>,
                          container: PrimitiveContainer) -> PrimitiveIndex {
         let prim_index = self.cpu_metadata.len();
         self.cpu_bounding_rects.push(None);
 
         let metadata = match container {
             PrimitiveContainer::Rectangle(rect) => {
-                let is_opaque = rect.color.a == 1.0;
-
                 let metadata = PrimitiveMetadata {
-                    is_opaque: is_opaque,
-                    clips: clips,
+                    opacity: PrimitiveOpacity::from_alpha(rect.color.a),
+                    clips,
                     clip_cache_info: clip_info,
                     prim_kind: PrimitiveKind::Rectangle,
                     cpu_prim_index: SpecificPrimitiveIndex(self.cpu_rectangles.len()),
                     gpu_location: GpuCacheHandle::new(),
                     render_task: None,
                     clip_task: None,
                     local_rect: *local_rect,
                     local_clip_rect: *local_clip_rect,
                 };
 
                 self.cpu_rectangles.push(rect);
 
                 metadata
             }
             PrimitiveContainer::TextRun(text_cpu) => {
                 let metadata = PrimitiveMetadata {
-                    is_opaque: false,
-                    clips: clips,
+                    opacity: PrimitiveOpacity::translucent(),
+                    clips,
                     clip_cache_info: clip_info,
                     prim_kind: PrimitiveKind::TextRun,
                     cpu_prim_index: SpecificPrimitiveIndex(self.cpu_text_runs.len()),
                     gpu_location: GpuCacheHandle::new(),
                     render_task: None,
                     clip_task: None,
                     local_rect: *local_rect,
                     local_clip_rect: *local_clip_rect,
                 };
 
                 self.cpu_text_runs.push(text_cpu);
                 metadata
             }
             PrimitiveContainer::Image(image_cpu) => {
                 let metadata = PrimitiveMetadata {
-                    is_opaque: false,
-                    clips: clips,
+                    opacity: PrimitiveOpacity::translucent(),
+                    clips,
                     clip_cache_info: clip_info,
                     prim_kind: PrimitiveKind::Image,
                     cpu_prim_index: SpecificPrimitiveIndex(self.cpu_images.len()),
                     gpu_location: GpuCacheHandle::new(),
                     render_task: None,
                     clip_task: None,
                     local_rect: *local_rect,
                     local_clip_rect: *local_clip_rect,
                 };
 
                 self.cpu_images.push(image_cpu);
                 metadata
             }
             PrimitiveContainer::YuvImage(image_cpu) => {
                 let metadata = PrimitiveMetadata {
-                    is_opaque: true,
-                    clips: clips,
+                    opacity: PrimitiveOpacity::opaque(),
+                    clips,
                     clip_cache_info: clip_info,
                     prim_kind: PrimitiveKind::YuvImage,
                     cpu_prim_index: SpecificPrimitiveIndex(self.cpu_yuv_images.len()),
                     gpu_location: GpuCacheHandle::new(),
                     render_task: None,
                     clip_task: None,
                     local_rect: *local_rect,
                     local_clip_rect: *local_clip_rect,
                 };
 
                 self.cpu_yuv_images.push(image_cpu);
                 metadata
             }
             PrimitiveContainer::Border(border_cpu) => {
                 let metadata = PrimitiveMetadata {
-                    is_opaque: false,
-                    clips: clips,
+                    opacity: PrimitiveOpacity::translucent(),
+                    clips,
                     clip_cache_info: clip_info,
                     prim_kind: PrimitiveKind::Border,
                     cpu_prim_index: SpecificPrimitiveIndex(self.cpu_borders.len()),
                     gpu_location: GpuCacheHandle::new(),
                     render_task: None,
                     clip_task: None,
                     local_rect: *local_rect,
                     local_clip_rect: *local_clip_rect,
                 };
 
                 self.cpu_borders.push(border_cpu);
                 metadata
             }
             PrimitiveContainer::AlignedGradient(gradient_cpu) => {
                 let metadata = PrimitiveMetadata {
-                    // TODO: calculate if the gradient is actually opaque
-                    is_opaque: false,
-                    clips: clips,
+                    opacity: PrimitiveOpacity::translucent(),
+                    clips,
                     clip_cache_info: clip_info,
                     prim_kind: PrimitiveKind::AlignedGradient,
                     cpu_prim_index: SpecificPrimitiveIndex(self.cpu_gradients.len()),
                     gpu_location: GpuCacheHandle::new(),
                     render_task: None,
                     clip_task: None,
                     local_rect: *local_rect,
                     local_clip_rect: *local_clip_rect,
                 };
 
                 self.cpu_gradients.push(gradient_cpu);
                 metadata
             }
             PrimitiveContainer::AngleGradient(gradient_cpu) => {
                 let metadata = PrimitiveMetadata {
                     // TODO: calculate if the gradient is actually opaque
-                    is_opaque: false,
-                    clips: clips,
+                    opacity: PrimitiveOpacity::translucent(),
+                    clips,
                     clip_cache_info: clip_info,
                     prim_kind: PrimitiveKind::AngleGradient,
                     cpu_prim_index: SpecificPrimitiveIndex(self.cpu_gradients.len()),
                     gpu_location: GpuCacheHandle::new(),
                     render_task: None,
                     clip_task: None,
                     local_rect: *local_rect,
                     local_clip_rect: *local_clip_rect,
                 };
 
                 self.cpu_gradients.push(gradient_cpu);
                 metadata
             }
             PrimitiveContainer::RadialGradient(radial_gradient_cpu) => {
                 let metadata = PrimitiveMetadata {
                     // TODO: calculate if the gradient is actually opaque
-                    is_opaque: false,
-                    clips: clips,
+                    opacity: PrimitiveOpacity::translucent(),
+                    clips,
                     clip_cache_info: clip_info,
                     prim_kind: PrimitiveKind::RadialGradient,
                     cpu_prim_index: SpecificPrimitiveIndex(self.cpu_radial_gradients.len()),
                     gpu_location: GpuCacheHandle::new(),
                     render_task: None,
                     clip_task: None,
                     local_rect: *local_rect,
                     local_clip_rect: *local_clip_rect,
@@ -864,18 +897,18 @@ impl PrimitiveStore {
                 // shadow shader needs to run on.
                 // TODO(gw): In the future, we can probably merge the box shadow
                 // primitive (stretch) shader with the generic cached primitive shader.
                 let render_task = RenderTask::new_prim_cache(cache_key,
                                                              cache_size,
                                                              PrimitiveIndex(prim_index));
 
                 let metadata = PrimitiveMetadata {
-                    is_opaque: false,
-                    clips: clips,
+                    opacity: PrimitiveOpacity::translucent(),
+                    clips,
                     clip_cache_info: clip_info,
                     prim_kind: PrimitiveKind::BoxShadow,
                     cpu_prim_index: SpecificPrimitiveIndex(self.cpu_box_shadows.len()),
                     gpu_location: GpuCacheHandle::new(),
                     render_task: Some(render_task),
                     clip_task: None,
                     local_rect: *local_rect,
                     local_clip_rect: *local_clip_rect,
@@ -899,50 +932,51 @@ impl PrimitiveStore {
         self.cpu_metadata.len()
     }
 
     pub fn build_bounding_rect(&mut self,
                                prim_index: PrimitiveIndex,
                                screen_rect: &DeviceIntRect,
                                layer_transform: &LayerToWorldTransform,
                                layer_combined_local_clip_rect: &LayerRect,
-                               device_pixel_ratio: f32) -> bool {
+                               device_pixel_ratio: f32) -> Option<(LayerRect, DeviceIntRect)> {
         let metadata = &self.cpu_metadata[prim_index.0];
+        let local_rect = metadata.local_rect
+                                 .intersection(&metadata.local_clip_rect)
+                                 .and_then(|rect| rect.intersection(layer_combined_local_clip_rect));
 
-        let bounding_rect = metadata.local_rect
-                                    .intersection(&metadata.local_clip_rect)
-                                    .and_then(|rect| rect.intersection(layer_combined_local_clip_rect))
-                                    .and_then(|ref local_rect| {
-            let xf_rect = TransformedRect::new(local_rect,
+        let bounding_rect = local_rect.and_then(|local_rect| {
+            let xf_rect = TransformedRect::new(&local_rect,
                                                layer_transform,
                                                device_pixel_ratio);
             xf_rect.bounding_rect.intersection(screen_rect)
         });
 
         self.cpu_bounding_rects[prim_index.0] = bounding_rect;
-        bounding_rect.is_some()
+        bounding_rect.map(|screen_bound| (local_rect.unwrap(), screen_bound))
     }
 
     /// Returns true if the bounding box needs to be updated.
     pub fn prepare_prim_for_render(&mut self,
                                    prim_index: PrimitiveIndex,
                                    resource_cache: &mut ResourceCache,
                                    gpu_cache: &mut GpuCache,
                                    layer_transform: &LayerToWorldTransform,
                                    device_pixel_ratio: f32,
-                                   display_list: &BuiltDisplayList) {
+                                   display_list: &BuiltDisplayList)
+                                   -> &mut PrimitiveMetadata {
 
         let metadata = &mut self.cpu_metadata[prim_index.0];
 
         if let Some(ref mut clip_info) = metadata.clip_cache_info {
-            clip_info.update(&metadata.clips,
-                             layer_transform,
-                             &mut self.gpu_data32,
-                             device_pixel_ratio,
-                             display_list);
+            clip_info.update(&metadata.clips, layer_transform, gpu_cache, device_pixel_ratio);
+
+            //TODO-LCCR: we could tighten up the `local_clip_rect` here
+            // but that would require invalidating the whole GPU block
+
             for clip in &metadata.clips {
                 if let ClipSource::Region(ClipRegion{ image_mask: Some(ref mask), .. }, ..) = *clip {
                     resource_cache.request_image(mask.image, ImageRendering::Auto, None);
                 }
             }
         }
 
         match metadata.prim_kind {
@@ -1015,44 +1049,41 @@ impl PrimitiveStore {
                     ImagePrimitiveKind::Image(image_key, image_rendering, tile_offset, tile_spacing) => {
                         resource_cache.request_image(image_key, image_rendering, tile_offset);
 
                         // TODO(gw): This doesn't actually need to be calculated each frame.
                         // It's cheap enough that it's not worth introducing a cache for images
                         // right now, but if we introduce a cache for images for some other
                         // reason then we might as well cache this with it.
                         let image_properties = resource_cache.get_image_properties(image_key);
-                        metadata.is_opaque = image_properties.descriptor.is_opaque &&
-                                             tile_spacing.width == 0.0 &&
-                                             tile_spacing.height == 0.0;
+                        metadata.opacity.is_opaque = image_properties.descriptor.is_opaque &&
+                                                     tile_spacing.width == 0.0 &&
+                                                     tile_spacing.height == 0.0;
                     }
                     ImagePrimitiveKind::WebGL(..) => {}
                 }
             }
             PrimitiveKind::YuvImage => {
                 let image_cpu = &mut self.cpu_yuv_images[metadata.cpu_prim_index.0];
 
                 let channel_num = image_cpu.format.get_plane_num();
                 debug_assert!(channel_num <= 3);
                 for channel in 0..channel_num {
                     resource_cache.request_image(image_cpu.yuv_key[channel], image_cpu.image_rendering, None);
                 }
-
-                // TODO(nical): Currently assuming no tile_spacing for yuv images.
-                metadata.is_opaque = true;
             }
             PrimitiveKind::AlignedGradient |
             PrimitiveKind::AngleGradient |
             PrimitiveKind::RadialGradient => {}
         }
 
         // Mark this GPU resource as required for this frame.
         if let Some(mut request) = gpu_cache.request(&mut metadata.gpu_location) {
-            request.push(metadata.local_rect.into());
-            request.push(metadata.local_clip_rect.into());
+            request.push(metadata.local_rect);
+            request.push(metadata.local_clip_rect);
 
             match metadata.prim_kind {
                 PrimitiveKind::Rectangle => {
                     let rect = &self.cpu_rectangles[metadata.cpu_prim_index.0];
                     rect.write_gpu_blocks(request);
                 }
                 PrimitiveKind::Border => {
                     let border = &self.cpu_borders[metadata.cpu_prim_index.0];
@@ -1067,18 +1098,18 @@ impl PrimitiveStore {
                     image.write_gpu_blocks(request);
                 }
                 PrimitiveKind::YuvImage => {
                     let yuv_image = &self.cpu_yuv_images[metadata.cpu_prim_index.0];
                     yuv_image.write_gpu_blocks(request);
                 }
                 PrimitiveKind::AlignedGradient => {
                     let gradient = &self.cpu_gradients[metadata.cpu_prim_index.0];
-                    gradient.build_gpu_blocks_for_aligned(display_list,
-                                                          request);
+                    metadata.opacity = gradient.build_gpu_blocks_for_aligned(display_list,
+                                                                             request);
                 }
                 PrimitiveKind::AngleGradient => {
                     let gradient = &self.cpu_gradients[metadata.cpu_prim_index.0];
                     gradient.build_gpu_blocks_for_angle_radial(display_list,
                                                                request);
                 }
                 PrimitiveKind::RadialGradient => {
                     let gradient = &self.cpu_radial_gradients[metadata.cpu_prim_index.0];
@@ -1086,51 +1117,22 @@ impl PrimitiveStore {
                                                                request);
                 }
                 PrimitiveKind::TextRun => {
                     let text = &self.cpu_text_runs[metadata.cpu_prim_index.0];
                     text.write_gpu_blocks(request);
                 }
             }
         }
+
+        metadata
     }
 }
 
 
-macro_rules! define_gpu_block {
-    ($name:ident: $ty:ty = $($derive:ident),* ) => (
-        #[derive(Clone)]
-        #[repr(C)]
-        pub struct $name {
-            data: $ty,
-        }
-
-        impl Default for $name {
-            fn default() -> $name {
-                $name {
-                    data: unsafe { mem::uninitialized() }
-                }
-            }
-        }
-
-        $(
-            impl From<$derive> for $name {
-                fn from(data: $derive) -> $name {
-                    unsafe { mem::transmute(data) }
-                }
-            }
-        )*
-    )
-}
-
-define_gpu_block!(GpuBlock32: [f32; 8] =
-    ClipCorner, ClipRect, ImageMaskData,
-    BorderCornerClipData, BorderCornerDashClipData, BorderCornerDotClipData
-);
-
 //Test for one clip region contains another
 trait InsideTest<T> {
     fn might_contain(&self, clip: &T) -> bool;
 }
 
 impl InsideTest<ComplexClipRegion> for ComplexClipRegion {
     // Returns true if clip is inside self, can return false negative
     fn might_contain(&self, clip: &ComplexClipRegion) -> bool {
--- a/gfx/webrender/src/profiler.rs
+++ b/gfx/webrender/src/profiler.rs
@@ -3,17 +3,17 @@
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 use debug_render::DebugRenderer;
 use device::{Device, GpuMarker, GpuSample, NamedTag};
 use euclid::{Point2D, Size2D, Rect, vec2};
 use std::collections::vec_deque::VecDeque;
 use std::f32;
 use std::mem;
-use webrender_traits::ColorF;
+use api::ColorF;
 use time::precise_time_ns;
 
 const GRAPH_WIDTH: f32 = 1024.0;
 const GRAPH_HEIGHT: f32 = 320.0;
 const GRAPH_PADDING: f32 = 8.0;
 const GRAPH_FRAME_HEIGHT: f32 = 16.0;
 const PROFILE_PADDING: f32 = 10.0;
 
@@ -40,17 +40,17 @@ trait ProfileCounter {
 pub struct IntProfileCounter {
     description: &'static str,
     value: usize,
 }
 
 impl IntProfileCounter {
     fn new(description: &'static str) -> IntProfileCounter {
         IntProfileCounter {
-            description: description,
+            description,
             value: 0,
         }
     }
 
     fn reset(&mut self) {
         self.value = 0;
     }
 
@@ -89,17 +89,17 @@ pub struct ResourceProfileCounter {
     description: &'static str,
     value: usize,
     size: usize,
 }
 
 impl ResourceProfileCounter {
     fn new(description: &'static str) -> ResourceProfileCounter {
         ResourceProfileCounter {
-            description: description,
+            description,
             value: 0,
             size: 0,
         }
     }
 
     #[allow(dead_code)]
     fn reset(&mut self) {
         self.value = 0;
@@ -129,19 +129,19 @@ pub struct TimeProfileCounter {
     description: &'static str,
     nanoseconds: u64,
     invert: bool,
 }
 
 impl TimeProfileCounter {
     pub fn new(description: &'static str, invert: bool) -> TimeProfileCounter {
         TimeProfileCounter {
-            description: description,
+            description,
             nanoseconds: 0,
-            invert: invert,
+            invert,
         }
     }
 
     fn reset(&mut self) {
         self.nanoseconds = 0;
     }
 
     #[allow(dead_code)]
@@ -190,23 +190,23 @@ pub struct AverageTimeProfileCounter {
     num_samples: u64,
     nanoseconds: u64,
     invert: bool,
 }
 
 impl AverageTimeProfileCounter {
     pub fn new(description: &'static str, invert: bool, average_over_ns: u64) -> AverageTimeProfileCounter {
         AverageTimeProfileCounter {
-            description: description,
-            average_over_ns: average_over_ns,
+            description,
+            average_over_ns,
             start_ns: precise_time_ns(),
             sum_ns: 0,
             num_samples: 0,
             nanoseconds: 0,
-            invert: invert,
+            invert,
         }
     }
 
     #[allow(dead_code)]
     fn reset(&mut self) {
         self.start_ns = precise_time_ns();
         self.nanoseconds = 0;
         self.sum_ns = 0;
@@ -420,17 +420,17 @@ struct GraphStats {
 struct ProfileGraph {
     max_samples: usize,
     values: VecDeque<f32>,
 }
 
 impl ProfileGraph {
     fn new(max_samples: usize) -> ProfileGraph {
         ProfileGraph {
-            max_samples: max_samples,
+            max_samples,
             values: VecDeque::new(),
         }
     }
 
     fn push(&mut self, ns: u64) {
         let ms = ns as f64 / 1000000.0;
         if self.values.len() == self.max_samples {
             self.values.pop_back();
@@ -551,18 +551,18 @@ impl GpuFrameCollection {
         }
     }
 
     fn push(&mut self, total_time: u64, samples: Vec<GpuSample<GpuProfileTag>>) {
         if self.frames.len() == 20 {
             self.frames.pop_back();
         }
         self.frames.push_front(GpuFrame {
-            total_time: total_time,
-            samples: samples,
+            total_time,
+            samples,
         });
     }
 }
 
 impl GpuFrameCollection {
     fn draw(&self,
             x: f32,
             y: f32,
--- a/gfx/webrender/src/record.rs
+++ b/gfx/webrender/src/record.rs
@@ -4,17 +4,17 @@
 
 use bincode::{Infinite, serialize};
 use std::fmt::Debug;
 use std::mem;
 use std::any::TypeId;
 use std::fs::File;
 use std::io::Write;
 use std::path::PathBuf;
-use webrender_traits::ApiMsg;
+use api::ApiMsg;
 use byteorder::{LittleEndian, WriteBytesExt};
 
 pub static WEBRENDER_RECORDING_HEADER: u64 = 0xbeefbeefbeefbe01u64;
 
 pub trait ApiRecordingReceiver: Send + Debug {
     fn write_msg(&mut self, frame: u32, msg: &ApiMsg);
     fn write_payload(&mut self, frame: u32, data: &[u8]);
 }
@@ -32,17 +32,17 @@ impl BinaryRecorder {
         let apimsg_type_id = unsafe {
             assert!(mem::size_of::<TypeId>() == mem::size_of::<u64>());
             mem::transmute::<TypeId, u64>(TypeId::of::<ApiMsg>())
         };
         file.write_u64::<LittleEndian>(WEBRENDER_RECORDING_HEADER).ok();
         file.write_u64::<LittleEndian>(apimsg_type_id).ok();
 
         BinaryRecorder {
-            file: file,
+            file,
         }
     }
 
     fn write_length_and_data(&mut self, data: &[u8]) {
         self.file.write_u32::<LittleEndian>(data.len() as u32).ok();
         self.file.write(data).ok();
     }
 }
--- a/gfx/webrender/src/render_backend.rs
+++ b/gfx/webrender/src/render_backend.rs
@@ -5,31 +5,31 @@
 use frame::Frame;
 use frame_builder::FrameBuilderConfig;
 use gpu_cache::GpuCache;
 use internal_types::{SourceTexture, ResultMsg, RendererFrame};
 use profiler::{BackendProfileCounters, GpuCacheProfileCounters, TextureCacheProfileCounters};
 use record::ApiRecordingReceiver;
 use resource_cache::ResourceCache;
 use scene::Scene;
-use std::collections::HashMap;
+use std::collections::{HashMap, HashSet};
 use std::sync::{Arc, Mutex};
 use std::sync::mpsc::Sender;
 use texture_cache::TextureCache;
 use time::precise_time_ns;
 use thread_profiler::register_thread_with_profiler;
 use rayon::ThreadPool;
 use webgl_types::{GLContextHandleWrapper, GLContextWrapper};
-use webrender_traits::channel::{MsgReceiver, PayloadReceiver, PayloadReceiverHelperMethods};
-use webrender_traits::channel::{PayloadSender, PayloadSenderHelperMethods};
-use webrender_traits::{ApiMsg, BlobImageRenderer, BuiltDisplayList, DeviceIntPoint};
-use webrender_traits::{DeviceUintPoint, DeviceUintRect, DeviceUintSize, IdNamespace, ImageData};
-use webrender_traits::{LayerPoint, PipelineId, RenderDispatcher, RenderNotifier};
-use webrender_traits::{VRCompositorCommand, VRCompositorHandler, WebGLCommand, WebGLContextId};
-use webrender_traits::{FontTemplate};
+use api::channel::{MsgReceiver, PayloadReceiver, PayloadReceiverHelperMethods};
+use api::channel::{PayloadSender, PayloadSenderHelperMethods};
+use api::{ApiMsg, BlobImageRenderer, BuiltDisplayList, DeviceIntPoint};
+use api::{DeviceUintPoint, DeviceUintRect, DeviceUintSize, IdNamespace, ImageData};
+use api::{LayerPoint, PipelineId, RenderDispatcher, RenderNotifier};
+use api::{VRCompositorCommand, VRCompositorHandler, WebGLCommand, WebGLContextId};
+use api::{FontTemplate};
 
 #[cfg(feature = "webgl")]
 use offscreen_gl_context::GLContextDispatcher;
 
 #[cfg(not(feature = "webgl"))]
 use webgl_types::GLContextDispatcher;
 
 /// The render backend is responsible for transforming high level display lists into
@@ -55,23 +55,31 @@ pub struct RenderBackend {
     resource_cache: ResourceCache,
 
     scene: Scene,
     frame: Frame,
 
     notifier: Arc<Mutex<Option<Box<RenderNotifier>>>>,
     webrender_context_handle: Option<GLContextHandleWrapper>,
     webgl_contexts: HashMap<WebGLContextId, GLContextWrapper>,
+    dirty_webgl_contexts: HashSet<WebGLContextId>,
     current_bound_webgl_context_id: Option<WebGLContextId>,
     recorder: Option<Box<ApiRecordingReceiver>>,
     main_thread_dispatcher: Arc<Mutex<Option<Box<RenderDispatcher>>>>,
 
     next_webgl_id: usize,
 
-    vr_compositor_handler: Arc<Mutex<Option<Box<VRCompositorHandler>>>>
+    vr_compositor_handler: Arc<Mutex<Option<Box<VRCompositorHandler>>>>,
+
+    // A helper switch to prevent any frames rendering triggered by scrolling
+    // messages between `SetDisplayList` and `GenerateFrame`.
+    // If we allow them, then a reftest that scrolls a few layers before generating
+    // the first frame would produce inconsistent rendering results, because
+    // scroll events are not necessarily received in deterministic order.
+    render_on_scroll: bool,
 }
 
 impl RenderBackend {
     pub fn new(api_rx: MsgReceiver<ApiMsg>,
                payload_rx: PayloadReceiver,
                payload_tx: PayloadSender,
                result_tx: Sender<ResultMsg>,
                hidpi_factor: f32,
@@ -86,39 +94,52 @@ impl RenderBackend {
                vr_compositor_handler: Arc<Mutex<Option<Box<VRCompositorHandler>>>>,
                initial_window_size: DeviceUintSize) -> RenderBackend {
 
         let resource_cache = ResourceCache::new(texture_cache, workers, blob_image_renderer);
 
         register_thread_with_profiler("Backend".to_string());
 
         RenderBackend {
-            api_rx: api_rx,
-            payload_rx: payload_rx,
-            payload_tx: payload_tx,
-            result_tx: result_tx,
-            hidpi_factor: hidpi_factor,
+            api_rx,
+            payload_rx,
+            payload_tx,
+            result_tx,
+            hidpi_factor,
             page_zoom_factor: 1.0,
             pinch_zoom_factor: 1.0,
             pan: DeviceIntPoint::zero(),
-            resource_cache: resource_cache,
+            resource_cache,
             gpu_cache: GpuCache::new(),
             scene: Scene::new(),
             frame: Frame::new(config),
             next_namespace_id: IdNamespace(1),
-            notifier: notifier,
-            webrender_context_handle: webrender_context_handle,
+            notifier,
+            webrender_context_handle,
             webgl_contexts: HashMap::new(),
+            dirty_webgl_contexts: HashSet::new(),
             current_bound_webgl_context_id: None,
-            recorder: recorder,
-            main_thread_dispatcher: main_thread_dispatcher,
+            recorder,
+            main_thread_dispatcher,
             next_webgl_id: 0,
-            vr_compositor_handler: vr_compositor_handler,
+            vr_compositor_handler,
             window_size: initial_window_size,
             inner_rect: DeviceUintRect::new(DeviceUintPoint::zero(), initial_window_size),
+            render_on_scroll: false,
+        }
+    }
+
+    fn scroll_frame(&mut self, frame_maybe: Option<RendererFrame>,
+                    profile_counters: &mut BackendProfileCounters) {
+        match frame_maybe {
+            Some(frame) => {
+                self.publish_frame(frame, profile_counters);
+                self.notify_compositor_of_new_scroll_frame(true)
+            }
+            None => self.notify_compositor_of_new_scroll_frame(false),
         }
     }
 
     pub fn run(&mut self, mut profile_counters: BackendProfileCounters) {
         let mut frame_counter: u32 = 0;
 
         loop {
             let msg = self.api_rx.recv();
@@ -227,16 +248,18 @@ impl RenderBackend {
                                                             epoch,
                                                             built_display_list,
                                                             background_color,
                                                             viewport_size,
                                                             content_size);
                                 self.build_scene();
                             });
 
+                            self.render_on_scroll = false; //wait for `GenerateFrame`
+
                             // Note: this isn't quite right as auxiliary values will be
                             // pulled out somewhere in the prim_store, but aux values are
                             // really simple and cheap to access, so it's not a big deal.
                             let display_list_consumed_time = precise_time_ns();
 
                             profile_counters.ipc.set(builder_start_time, builder_finish_time,
                                                      display_list_received_time, display_list_consumed_time,
                                                      display_list_len);
@@ -254,70 +277,61 @@ impl RenderBackend {
                             })
                         }
                         ApiMsg::Scroll(delta, cursor, move_phase) => {
                             profile_scope!("Scroll");
                             let frame = {
                                 let counters = &mut profile_counters.resources.texture_cache;
                                 let gpu_cache_counters = &mut profile_counters.resources.gpu_cache;
                                 profile_counters.total_time.profile(|| {
-                                    if self.frame.scroll(delta, cursor, move_phase) {
+                                    if self.frame.scroll(delta, cursor, move_phase) && self.render_on_scroll {
                                         Some(self.render(counters, gpu_cache_counters))
                                     } else {
                                         None
                                     }
                                 })
                             };
 
-                            match frame {
-                                Some(frame) => {
-                                    self.publish_frame(frame, &mut profile_counters);
-                                    self.notify_compositor_of_new_scroll_frame(true)
-                                }
-                                None => self.notify_compositor_of_new_scroll_frame(false),
-                            }
+                            self.scroll_frame(frame, &mut profile_counters);
                         }
                         ApiMsg::ScrollNodeWithId(origin, id, clamp) => {
                             profile_scope!("ScrollNodeWithScrollId");
                             let frame = {
                                 let counters = &mut profile_counters.resources.texture_cache;
                                 let gpu_cache_counters = &mut profile_counters.resources.gpu_cache;
                                 profile_counters.total_time.profile(|| {
-                                    if self.frame.scroll_node(origin, id, clamp) {
+                                    if self.frame.scroll_node(origin, id, clamp) && self.render_on_scroll {
                                         Some(self.render(counters, gpu_cache_counters))
                                     } else {
                                         None
                                     }
                                 })
                             };
 
-                            match frame {
-                                Some(frame) => {
-                                    self.publish_frame(frame, &mut profile_counters);
-                                    self.notify_compositor_of_new_scroll_frame(true)
-                                }
-                                None => self.notify_compositor_of_new_scroll_frame(false),
-                            }
-
+                            self.scroll_frame(frame, &mut profile_counters);
                         }
                         ApiMsg::TickScrollingBounce => {
                             profile_scope!("TickScrollingBounce");
                             let frame = {
                                 let counters = &mut profile_counters.resources.texture_cache;
                                 let gpu_cache_counters = &mut profile_counters.resources.gpu_cache;
                                 profile_counters.total_time.profile(|| {
                                     self.frame.tick_scrolling_bounce_animations();
-                                    self.render(counters, gpu_cache_counters)
+                                    if self.render_on_scroll {
+                                        Some(self.render(counters, gpu_cache_counters))
+                                    } else {
+                                        None
+                                    }
                                 })
                             };
 
-                            self.publish_frame_and_notify_compositor(frame, &mut profile_counters);
+                            self.scroll_frame(frame, &mut profile_counters);
                         }
                         ApiMsg::TranslatePointToLayerSpace(..) => {
-                            panic!("unused api - remove from webrender_traits");
+                            panic!("unused api - remove from webrender_api");
                         }
                         ApiMsg::GetScrollNodeState(tx) => {
                             profile_scope!("GetScrollNodeState");
                             tx.send(self.frame.get_scroll_node_state()).unwrap()
                         }
                         ApiMsg::RequestWebGLContext(size, attributes, tx) => {
                             if let Some(ref wrapper) = self.webrender_context_handle {
                                 let dispatcher: Option<Box<GLContextDispatcher>> = if cfg!(target_os = "windows") {
@@ -340,16 +354,17 @@ impl RenderBackend {
 
                                         let (real_size, texture_id, limits) = ctx.get_info();
 
                                         self.webgl_contexts.insert(id, ctx);
 
                                         self.resource_cache
                                             .add_webgl_texture(id, SourceTexture::WebGL(texture_id),
                                                                real_size);
+                                        self.dirty_webgl_contexts.insert(id);
 
                                         tx.send(Ok((id, limits))).unwrap();
                                     },
                                     Err(msg) => {
                                         tx.send(Err(msg.to_owned())).unwrap();
                                     }
                                 }
                             } else {
@@ -364,39 +379,42 @@ impl RenderBackend {
                             }
                             match ctx.resize(&size) {
                                 Ok(_) => {
                                     // Update webgl texture size. Texture id may change too.
                                     let (real_size, texture_id, _) = ctx.get_info();
                                     self.resource_cache
                                         .update_webgl_texture(context_id, SourceTexture::WebGL(texture_id),
                                                               real_size);
+                                    self.dirty_webgl_contexts.insert(context_id);
                                 },
                                 Err(msg) => {
                                     error!("Error resizing WebGLContext: {}", msg);
                                 }
                             }
                         }
                         ApiMsg::WebGLCommand(context_id, command) => {
                             // TODO: Buffer the commands and only apply them here if they need to
                             // be synchronous.
                             let ctx = &self.webgl_contexts[&context_id];
                             if Some(context_id) != self.current_bound_webgl_context_id {
                                 ctx.make_current();
                                 self.current_bound_webgl_context_id = Some(context_id);
                             }
                             ctx.apply_command(command);
+                            self.dirty_webgl_contexts.insert(context_id);
                         },
 
                         ApiMsg::VRCompositorCommand(context_id, command) => {
                             if Some(context_id) != self.current_bound_webgl_context_id {
                                 self.webgl_contexts[&context_id].make_current();
                                 self.current_bound_webgl_context_id = Some(context_id);
                             }
                             self.handle_vr_compositor_command(context_id, command);
+                            self.dirty_webgl_contexts.insert(context_id);
                         }
                         ApiMsg::GenerateFrame(property_bindings) => {
                             profile_scope!("GenerateFrame");
 
                             // Ideally, when there are property bindings present,
                             // we won't need to rebuild the entire frame here.
                             // However, to avoid conflicts with the ongoing work to
                             // refactor how scroll roots + transforms work, this
@@ -408,16 +426,18 @@ impl RenderBackend {
                             //           rebuild of the frame!
                             if let Some(property_bindings) = property_bindings {
                                 self.scene.properties.set_properties(property_bindings);
                                 profile_counters.total_time.profile(|| {
                                     self.build_scene();
                                 });
                             }
 
+                            self.render_on_scroll = true;
+
                             let frame = {
                                 let counters = &mut profile_counters.resources.texture_cache;
                                 let gpu_cache_counters = &mut profile_counters.resources.gpu_cache;
                                 profile_counters.total_time.profile(|| {
                                     self.render(counters, gpu_cache_counters)
                                 })
                             };
                             if self.scene.root_pipeline_id.is_some() {
@@ -471,20 +491,28 @@ impl RenderBackend {
 
         // When running in OSMesa mode with texture sharing,
         // a flush is required on any GL contexts to ensure
         // that read-back from the shared texture returns
         // valid data! This should be fine to have run on all
         // implementations - a single flush for each webgl
         // context at the start of a render frame should
         // incur minimal cost.
-        for (_, webgl_context) in &self.webgl_contexts {
-            webgl_context.make_current();
-            webgl_context.apply_command(WebGLCommand::Flush);
-            webgl_context.unbind();
+        // glFlush is not enough in some GPUs.
+        // glFlush doesn't guarantee the completion of the GL commands when the shared texture is sampled.
+        // This leads to some graphic glitches on some demos or even nothing being rendered at all (GPU Mali-T880).
+        // glFinish guarantees the completion of the commands but it may hurt performance a lot.
+        // Sync Objects are the recommended way to ensure that textures are ready in OpenGL 3.0+.
+        // They are more performant than glFinish and guarantee the completion of the GL commands.
+        for (id, webgl_context) in &self.webgl_contexts {
+            if self.dirty_webgl_contexts.remove(&id) {
+                webgl_context.make_current();
+                webgl_context.apply_command(WebGLCommand::FenceAndWaitSync);
+                webgl_context.unbind();
+            }
         }
 
         let accumulated_scale_factor = self.accumulated_scale_factor();
         self.frame.create(&self.scene,
                           &mut self.resource_cache,
                           self.window_size,
                           self.inner_rect,
                           accumulated_scale_factor);
--- a/gfx/webrender/src/render_task.rs
+++ b/gfx/webrender/src/render_task.rs
@@ -1,21 +1,21 @@
 /* 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 gpu_cache::GpuCacheHandle;
 use internal_types::{HardwareCompositeOp, LowLevelFilterOp};
-use mask_cache::{MaskBounds, MaskCacheInfo};
+use mask_cache::MaskCacheInfo;
 use prim_store::{PrimitiveCacheKey, PrimitiveIndex};
 use std::{cmp, f32, i32, mem, usize};
 use tiling::{ClipScrollGroupIndex, PackedLayerIndex, RenderPass, RenderTargetIndex};
 use tiling::{RenderTargetKind, StackingContextIndex};
-use webrender_traits::{ClipId, DeviceIntLength, DeviceIntPoint, DeviceIntRect, DeviceIntSize};
-use webrender_traits::{MixBlendMode};
+use api::{ClipId, DeviceIntLength, DeviceIntPoint, DeviceIntRect, DeviceIntSize};
+use api::{MixBlendMode};
 
 const FLOATS_PER_RENDER_TASK_INFO: usize = 12;
 
 #[derive(Debug, Copy, Clone, Eq, Hash, PartialEq)]
 pub struct RenderTaskIndex(pub usize);
 
 #[derive(Debug, Copy, Clone, Eq, PartialEq, Hash)]
 pub enum RenderTaskKey {
@@ -79,32 +79,26 @@ pub enum MaskSegment {
 #[derive(Debug, Copy, Clone)]
 #[repr(C)]
 pub enum MaskGeometryKind {
     Default,        // Draw the entire rect
     CornersOnly,    // Draw the corners (simple axis aligned mask)
     // TODO(gw): Add more types here (e.g. 4 rectangles outside the inner rect)
 }
 
+pub type ClipWorkItem = (PackedLayerIndex, MaskCacheInfo);
+
 #[derive(Debug, Clone)]
 pub struct CacheMaskTask {
     actual_rect: DeviceIntRect,
     inner_rect: DeviceIntRect,
-    pub clips: Vec<(PackedLayerIndex, MaskCacheInfo)>,
+    pub clips: Vec<ClipWorkItem>,
     pub geometry_kind: MaskGeometryKind,
 }
 
-#[derive(Debug)]
-pub enum MaskResult {
-    /// The mask is completely outside the region
-    Outside,
-    /// The mask is inside and needs to be processed
-    Inside(RenderTask),
-}
-
 #[derive(Debug, Clone)]
 pub struct RenderTaskData {
     pub data: [f32; FLOATS_PER_RENDER_TASK_INFO],
 }
 
 impl RenderTaskData {
     pub fn empty() -> RenderTaskData {
         RenderTaskData {
@@ -144,19 +138,19 @@ pub struct RenderTask {
 
 impl RenderTask {
     pub fn new_alpha_batch(task_index: RenderTaskIndex,
                            screen_origin: DeviceIntPoint,
                            location: RenderTaskLocation) -> RenderTask {
         RenderTask {
             id: RenderTaskId::Static(task_index),
             children: Vec::new(),
-            location: location,
+            location,
             kind: RenderTaskKind::Alpha(AlphaRenderTask {
-                screen_origin: screen_origin,
+                screen_origin,
                 items: Vec::new(),
             }),
         }
     }
 
     pub fn new_dynamic_alpha_batch(task_index: RenderTaskIndex,
                                    rect: &DeviceIntRect) -> RenderTask {
         let location = RenderTaskLocation::Dynamic(None, rect.size);
@@ -179,93 +173,68 @@ impl RenderTask {
         RenderTask {
             id: RenderTaskId::Dynamic(RenderTaskKey::CopyFramebuffer(key)),
             children: Vec::new(),
             location: RenderTaskLocation::Dynamic(None, screen_rect.size),
             kind: RenderTaskKind::Readback(screen_rect),
         }
     }
 
-    pub fn new_mask(actual_rect: DeviceIntRect,
+    pub fn new_mask(task_rect: DeviceIntRect,
                     mask_key: MaskCacheKey,
-                    clips: &[(PackedLayerIndex, MaskCacheInfo)])
-                    -> MaskResult {
-        if clips.is_empty() {
-            return MaskResult::Outside;
-        }
-
-        // We scan through the clip stack and detect if our actual rectangle
-        // is in the intersection of all of all the outer bounds,
-        // and if it's completely inside the intersection of all of the inner bounds.
-
-        // TODO(gw): If we encounter a clip with unknown bounds, we'll just use
-        // the original rect. This is overly conservative, but can
-        // be optimized later.
-        let mut result = Some(actual_rect);
-        for &(_, ref clip) in clips {
-            match *clip.bounds.as_ref().unwrap() {
-                MaskBounds::OuterInner(ref outer, _) |
-                MaskBounds::Outer(ref outer) => {
-                    result = result.and_then(|rect| {
-                        rect.intersection(&outer.bounding_rect)
-                    });
+                    raw_clips: &[ClipWorkItem],
+                    extra_clip: Option<ClipWorkItem>)
+                    -> Option<RenderTask> {
+        /// Filter out all the clip instances that don't contribute to the result
+        let mut inner_rect = Some(task_rect);
+        let clips: Vec<_> = raw_clips.iter()
+                                     .chain(extra_clip.iter())
+                                     .filter(|&&(_, ref clip_info)| {
+            match clip_info.bounds.inner {
+                Some(ref inner) if !inner.device_rect.is_empty() => {
+                    inner_rect = inner_rect.and_then(|r| r.intersection(&inner.device_rect));
+                    !inner.device_rect.contains_rect(&task_rect)
                 }
-                MaskBounds::None => {
-                    result = Some(actual_rect);
-                    break;
+                _ => {
+                    inner_rect = None;
+                    true
                 }
             }
-        }
-
-        let task_rect = match result {
-            None => return MaskResult::Outside,
-            Some(rect) => rect,
-        };
+        }).cloned().collect();
 
-        // Accumulate inner rects. As soon as we encounter
-        // a clip mask where we don't have or don't know
-        // the inner rect, this will become None.
-        let inner_rect = clips.iter()
-                              .fold(Some(task_rect), |current, clip| {
-            current.and_then(|rect| {
-                let inner_rect = match *clip.1.bounds.as_ref().unwrap() {
-                    MaskBounds::Outer(..) |
-                    MaskBounds::None => DeviceIntRect::zero(),
-                    MaskBounds::OuterInner(_, ref inner) => inner.bounding_rect
-                };
-                rect.intersection(&inner_rect)
-            })
-        });
+        // Nothing to do, all clips are irrelevant for this case
+        if clips.is_empty() {
+            return None
+        }
 
         // TODO(gw): This optimization is very conservative for now.
         //           For now, only draw optimized geometry if it is
         //           a single aligned rect mask with rounded corners.
         //           In the future, we'll expand this to handle the
         //           more complex types of clip mask geometry.
         let mut geometry_kind = MaskGeometryKind::Default;
         if inner_rect.is_some() && clips.len() == 1 {
-            let (_, ref clip_info) = clips[0];
-            if clip_info.image.is_none() &&
-               clip_info.effective_complex_clip_count == 1 &&
-               clip_info.is_aligned {
+            let (_, ref info) = clips[0];
+            if info.border_corners.is_empty() &&
+               info.image.is_none() &&
+               info.complex_clip_range.get_count() == 1 &&
+               info.layer_clip_range.get_count() == 0 {
                 geometry_kind = MaskGeometryKind::CornersOnly;
             }
         }
 
-        let inner_rect = inner_rect.unwrap_or(DeviceIntRect::zero());
-
-        MaskResult::Inside(RenderTask {
+        Some(RenderTask {
             id: RenderTaskId::Dynamic(RenderTaskKey::CacheMask(mask_key)),
             children: Vec::new(),
             location: RenderTaskLocation::Dynamic(None, task_rect.size),
             kind: RenderTaskKind::CacheMask(CacheMaskTask {
                 actual_rect: task_rect,
-                inner_rect: inner_rect,
-                clips: clips.to_vec(),
-                geometry_kind: geometry_kind,
+                inner_rect: inner_rect.unwrap_or(DeviceIntRect::zero()),
+                clips,
+                geometry_kind,
             }),
         })
     }
 
     // Construct a render task to apply a blur to a primitive. For now,
     // this is only used for text runs, but we can probably extend this
     // to handle general blurs to any render task in the future.
     // The render task chain that is constructed looks like:
--- a/gfx/webrender/src/renderer.rs
+++ b/gfx/webrender/src/renderer.rs
@@ -6,25 +6,24 @@
 //!
 //! The `webrender::renderer` module provides the interface to webrender, which
 //! is accessible through [`Renderer`][renderer]
 //!
 //! [renderer]: struct.Renderer.html
 
 use debug_colors;
 use debug_render::DebugRenderer;
-use device::{DepthFunction, Device, FrameId, ProgramId, TextureId, VertexFormat, GpuMarker, GpuProfiler};
+use device::{DepthFunction, Device, FrameId, ProgramId, TextureId, VertexFormat, GpuMarker, GpuProfiler, PBOId};
 use device::{GpuSample, TextureFilter, VAOId, VertexUsageHint, FileWatcherHandler, TextureTarget, ShaderError};
 use device::get_gl_format_bgra;
 use euclid::Transform3D;
 use fnv::FnvHasher;
 use frame_builder::FrameBuilderConfig;
 use gleam::gl;
 use gpu_cache::{GpuBlockData, GpuCacheUpdate, GpuCacheUpdateList};
-use gpu_store::{GpuStore, GpuStoreLayout};
 use internal_types::{CacheTextureId, RendererFrame, ResultMsg, TextureUpdateOp};
 use internal_types::{TextureUpdateList, PackedVertex, RenderTargetMode};
 use internal_types::{ORTHO_NEAR_PLANE, ORTHO_FAR_PLANE, SourceTexture};
 use internal_types::{BatchTextures, TextureSampler};
 use profiler::{Profiler, BackendProfileCounters};
 use profiler::{GpuProfileTag, RendererProfileTimers, RendererProfileCounters};
 use record::ApiRecordingReceiver;
 use render_backend::RenderBackend;
@@ -33,36 +32,35 @@ use std;
 use std::cmp;
 use std::collections::{HashMap, VecDeque};
 use std::f32;
 use std::hash::BuildHasherDefault;
 use std::marker::PhantomData;
 use std::mem;
 use std::path::PathBuf;
 use std::rc::Rc;
-use std::slice;
 use std::sync::{Arc, Mutex};
 use std::sync::mpsc::{channel, Receiver, Sender};
 use std::thread;
 use texture_cache::TextureCache;
 use rayon::ThreadPool;
 use rayon::Configuration as ThreadPoolConfig;
 use tiling::{AlphaBatchKind, BlurCommand, CompositePrimitiveInstance, Frame, PrimitiveBatch, RenderTarget};
 use tiling::{AlphaRenderTarget, CacheClipInstance, PrimitiveInstance, ColorRenderTarget, RenderTargetKind};
 use time::precise_time_ns;
 use thread_profiler::{register_thread_with_profiler, write_profile};
 use util::TransformedRectKind;
 use webgl_types::GLContextHandleWrapper;
-use webrender_traits::{ColorF, Epoch, PipelineId, RenderNotifier, RenderDispatcher};
-use webrender_traits::{ExternalImageId, ExternalImageType, ImageData, ImageFormat, RenderApiSender};
-use webrender_traits::{DeviceIntRect, DeviceUintRect, DeviceIntPoint, DeviceIntSize, DeviceUintSize};
-use webrender_traits::{BlobImageRenderer, channel, FontRenderMode};
-use webrender_traits::VRCompositorHandler;
-use webrender_traits::{YuvColorSpace, YuvFormat};
-use webrender_traits::{YUV_COLOR_SPACES, YUV_FORMATS};
+use api::{ColorF, Epoch, PipelineId, RenderNotifier, RenderDispatcher};
+use api::{ExternalImageId, ExternalImageType, ImageData, ImageFormat, RenderApiSender};
+use api::{DeviceIntRect, DeviceUintRect, DeviceIntPoint, DeviceIntSize, DeviceUintSize};
+use api::{BlobImageRenderer, channel, FontRenderMode};
+use api::VRCompositorHandler;
+use api::{YuvColorSpace, YuvFormat};
+use api::{YUV_COLOR_SPACES, YUV_FORMATS};
 
 pub const GPU_DATA_TEXTURE_POOL: usize = 5;
 pub const MAX_VERTEX_TEXTURE_WIDTH: usize = 1024;
 
 const GPU_TAG_CACHE_BOX_SHADOW: GpuProfileTag = GpuProfileTag { label: "C_BoxShadow", color: debug_colors::BLACK };
 const GPU_TAG_CACHE_CLIP: GpuProfileTag = GpuProfileTag { label: "C_Clip", color: debug_colors::PURPLE };
 const GPU_TAG_CACHE_TEXT_RUN: GpuProfileTag = GpuProfileTag { label: "C_TextRun", color: debug_colors::MISTYROSE };
 const GPU_TAG_SETUP_TARGET: GpuProfileTag = GpuProfileTag { label: "target", color: debug_colors::SLATEGREY };
@@ -151,148 +149,223 @@ pub struct GpuProfile {
 
 impl GpuProfile {
     fn new<T>(frame_id: FrameId, samples: &[GpuSample<T>]) -> GpuProfile {
         let mut paint_time_ns = 0;
         for sample in samples {
             paint_time_ns += sample.time_ns;
         }
         GpuProfile {
-            frame_id: frame_id,
-            paint_time_ns: paint_time_ns,
+            frame_id,
+            paint_time_ns,
         }
     }
 }
 
 #[derive(Debug)]
 pub struct CpuProfile {
     pub frame_id: FrameId,
     pub composite_time_ns: u64,
     pub draw_calls: usize,
 }
 
 impl CpuProfile {
     fn new(frame_id: FrameId,
            composite_time_ns: u64,
            draw_calls: usize) -> CpuProfile {
         CpuProfile {
-            frame_id: frame_id,
-            composite_time_ns: composite_time_ns,
-            draw_calls: draw_calls,
+            frame_id,
+            composite_time_ns,
+            draw_calls,
         }
     }
 }
 
 #[derive(Debug, Copy, Clone, PartialEq)]
 pub enum BlendMode {
     None,
     Alpha,
     PremultipliedAlpha,
 
     // Use the color of the text itself as a constant color blend factor.
     Subpixel(ColorF),
 }
 
+// Tracks the state of each row in the GPU cache texture.
+struct CacheRow {
+    is_dirty: bool,
+}
+
+impl CacheRow {
+    fn new() -> CacheRow {
+        CacheRow {
+            is_dirty: false,
+        }
+    }
+}
+
 /// The device-specific representation of the cache texture in gpu_cache.rs
 struct CacheTexture {
-    current_id: TextureId,
-    next_id: TextureId,
+    texture_id: TextureId,
+    pbo_id: PBOId,
+    rows: Vec<CacheRow>,
+    cpu_blocks: Vec<GpuBlockData>,
 }
 
 impl CacheTexture {
     fn new(device: &mut Device) -> CacheTexture {
-        let ids = device.create_texture_ids(2, TextureTarget::Default);
+        let texture_id = device.create_texture_ids(1, TextureTarget::Default)[0];
+        let pbo_id = device.create_pbo();
 
         CacheTexture {
-            current_id: ids[0],
-            next_id: ids[1],
+            texture_id,
+            pbo_id,
+            rows: Vec::new(),
+            cpu_blocks: Vec::new(),
         }
     }
 
     fn apply_patch(&mut self,
-                   device: &mut Device,
                    update: &GpuCacheUpdate,
                    blocks: &[GpuBlockData]) {
         match update {
             &GpuCacheUpdate::Copy { block_index, block_count, address } => {
-                // Apply an incremental update to the cache texture.
-                // TODO(gw): For the initial implementation, we will just
-                //           use update_texture() since it's simple. If / when
-                //           we profile this and find it to be slow on some / all
-                //           devices - we can look into other options, such as
-                //           using glMapBuffer() with the unsynchronized bit,
-                //           and managing the synchronization ourselves with fences.
-                let data: &[u8] = unsafe {
-                    let ptr = blocks.as_ptr()
-                                    .offset(block_index as isize);
-                    slice::from_raw_parts(ptr as *const _, block_count * 16)
-                };
-                device.update_texture(self.current_id,
-                                      address.u as u32,
-                                      address.v as u32,
-                                      block_count as u32,
-                                      1,
-                                      None,
-                                      data);
+                let row = address.v as usize;
+
+                // Ensure that the CPU-side shadow copy of the GPU cache data has enough
+                // rows to apply this patch.
+                while self.rows.len() <= row {
+                    // Add a new row.
+                    self.rows.push(CacheRow::new());
+                    // Add enough GPU blocks for this row.
+                    self.cpu_blocks.extend_from_slice(&[GpuBlockData::empty(); MAX_VERTEX_TEXTURE_WIDTH]);
+                }
+
+                // This row is dirty (needs to be updated in GPU texture).
+                self.rows[row].is_dirty = true;
+
+                // Copy the blocks from the patch array in the shadow CPU copy.
+                let block_offset = row * MAX_VERTEX_TEXTURE_WIDTH + address.u as usize;
+                let data = &mut self.cpu_blocks[block_offset..(block_offset + block_count)];
+                for i in 0..block_count {
+                    data[i] = blocks[block_index + i];
+                }
             }
         }
     }
 
     fn update(&mut self, device: &mut Device, updates: &GpuCacheUpdateList) {
         // See if we need to create or resize the texture.
-        let current_dimensions = device.get_texture_dimensions(self.current_id);
+        let current_dimensions = device.get_texture_dimensions(self.texture_id);
         if updates.height > current_dimensions.height {
             // Create a f32 texture that can be used for the vertex shader
             // to fetch data from.
-            device.init_texture(self.next_id,
+            device.init_texture(self.texture_id,
                                 MAX_VERTEX_TEXTURE_WIDTH as u32,
                                 updates.height as u32,
                                 ImageFormat::RGBAF32,
                                 TextureFilter::Nearest,
-                                RenderTargetMode::SimpleRenderTarget,
+                                RenderTargetMode::None,
                                 None);
 
             // Copy the current texture into the newly resized texture.
             if current_dimensions.height > 0 {
-                device.bind_draw_target(Some((self.next_id, 0)), None);
-
-                let blit_rect = DeviceIntRect::new(DeviceIntPoint::zero(),
-                                                   DeviceIntSize::new(MAX_VERTEX_TEXTURE_WIDTH as i32,
-                                                                      current_dimensions.height as i32));
-
-                // TODO(gw): Should probably switch this to glCopyTexSubImage2D, since we
-                // don't do any stretching here.
-                device.blit_render_target(Some((self.current_id, 0)),
-                                          Some(blit_rect),
-                                          blit_rect);
-
-                // Free the GPU memory for that texture until we need to resize again.
-                device.deinit_texture(self.current_id);
+                // If we had to resize the texture, just mark all rows
+                // as dirty so they will be uploaded to the texture
+                // during the next flush.
+                for row in &mut self.rows {
+                    row.is_dirty = true;
+                }
             }
-
-            mem::swap(&mut self.current_id, &mut self.next_id);
         }
 
         for update in &updates.updates {
-            self.apply_patch(device, update, &updates.blocks);
+            self.apply_patch(update, &updates.blocks);
+        }
+    }
+
+    fn flush(&mut self, device: &mut Device) {
+        // Bind a PBO to do the texture upload.
+        // Updating the texture via PBO avoids CPU-side driver stalls.
+        device.bind_pbo(Some(self.pbo_id));
+
+        for (row_index, row) in self.rows.iter_mut().enumerate() {
+            if row.is_dirty {
+                // Get the data for this row and push to the PBO.
+                let block_index = row_index * MAX_VERTEX_TEXTURE_WIDTH;
+                let cpu_blocks = &self.cpu_blocks[block_index..(block_index + MAX_VERTEX_TEXTURE_WIDTH)];
+                device.update_pbo_data(cpu_blocks);
+
+                // Insert a command to copy the PBO data to the right place in
+                // the GPU-side cache texture.
+                device.update_texture_from_pbo(self.texture_id,
+                                               0,
+                                               row_index as u32,
+                                               MAX_VERTEX_TEXTURE_WIDTH as u32,
+                                               1,
+                                               0);
+
+                // Orphan the PBO. This is the recommended way to hint to the
+                // driver to detach the underlying storage from this PBO id.
+                // Keeping the size the same gives the driver a hint for future
+                // use of this PBO.
+                device.orphan_pbo(mem::size_of::<GpuBlockData>() * MAX_VERTEX_TEXTURE_WIDTH);
+
+                row.is_dirty = false;
+            }
         }
+
+        // Ensure that other texture updates won't read from this PBO.
+        device.bind_pbo(None);
+    }
+}
+
+
+trait GpuStoreLayout {
+    fn image_format() -> ImageFormat;
+
+    fn texture_width<T>() -> usize;
+
+    fn texture_filter() -> TextureFilter;
+
+    fn texel_size() -> usize {
+        match Self::image_format() {
+            ImageFormat::BGRA8 => 4,
+            ImageFormat::RGBAF32 => 16,
+            _ => unreachable!(),
+        }
+    }
+
+    fn texels_per_item<T>() -> usize {
+        let item_size = mem::size_of::<T>();
+        let texel_size = Self::texel_size();
+        debug_assert!(item_size % texel_size == 0);
+        item_size / texel_size
+    }
+
+    fn items_per_row<T>() -> usize {
+        Self::texture_width::<T>() / Self::texels_per_item::<T>()
+    }
+
+    fn rows_per_item<T>() -> usize {
+        Self::texels_per_item::<T>() / Self::texture_width::<T>()
     }
 }
 
 struct GpuDataTexture<L> {
     id: TextureId,
     layout: PhantomData<L>,
 }
 
 impl<L: GpuStoreLayout> GpuDataTexture<L> {
     fn new(device: &mut Device) -> GpuDataTexture<L> {
         let id = device.create_texture_ids(1, TextureTarget::Default)[0];
 
         GpuDataTexture {
-            id: id,
+            id,
             layout: PhantomData,
         }
     }
 
     fn init<T: Default>(&mut self,
                         device: &mut Device,
                         data: &mut Vec<T>) {
         if data.is_empty() {
@@ -339,21 +412,20 @@ impl GpuStoreLayout for VertexDataTextur
     }
 
     fn texture_filter() -> TextureFilter {
         TextureFilter::Nearest
     }
 }
 
 type VertexDataTexture = GpuDataTexture<VertexDataTextureLayout>;
-pub type VertexDataStore<T> = GpuStore<T, VertexDataTextureLayout>;
 
-const TRANSFORM_FEATURE: &'static str = "TRANSFORM";
-const SUBPIXEL_AA_FEATURE: &'static str = "SUBPIXEL_AA";
-const CLIP_FEATURE: &'static str = "CLIP";
+const TRANSFORM_FEATURE: &str = "TRANSFORM";
+const SUBPIXEL_AA_FEATURE: &str = "SUBPIXEL_AA";
+const CLIP_FEATURE: &str = "CLIP";
 
 enum ShaderKind {
     Primitive,
     Cache(VertexFormat),
     ClipCache,
 }
 
 struct LazilyCompiledShader {
@@ -366,18 +438,18 @@ struct LazilyCompiledShader {
 impl LazilyCompiledShader {
     fn new(kind: ShaderKind,
            name: &'static str,
            features: &[&'static str],
            device: &mut Device,
            precache: bool) -> Result<LazilyCompiledShader, ShaderError> {
         let mut shader = LazilyCompiledShader {
             id: None,
-            name: name,
-            kind: kind,
+            name,
+            kind,
             features: features.to_vec(),
         };
 
         if precache {
             try!{ shader.get(device) };
         }
 
         Ok(shader)
@@ -460,18 +532,18 @@ impl PrimitiveShader {
             LazilyCompiledShader::new(ShaderKind::Primitive,
                                       name,
                                       &transform_features,
                                       device,
                                       precache)
         };
 
         Ok(PrimitiveShader {
-            simple: simple,
-            transform: transform,
+            simple,
+            transform,
         })
     }
 
     fn get(&mut self,
            device: &mut Device,
            transform_kind: TransformedRectKind) -> Result<ProgramId, ShaderError> {
         match transform_kind {
             TransformedRectKind::AxisAligned => self.simple.get(device),
@@ -506,36 +578,32 @@ fn create_clip_shader(name: &'static str
 
     let includes = &["prim_shared", "clip_shared"];
     device.create_program_with_prefix(name, includes, Some(prefix), VertexFormat::Clip)
 }
 
 struct GpuDataTextures {
     layer_texture: VertexDataTexture,
     render_task_texture: VertexDataTexture,
-    data32_texture: VertexDataTexture,
 }
 
 impl GpuDataTextures {
     fn new(device: &mut Device) -> GpuDataTextures {
         GpuDataTextures {
             layer_texture: VertexDataTexture::new(device),
             render_task_texture: VertexDataTexture::new(device),
-            data32_texture: VertexDataTexture::new(device),
         }
     }
 
     fn init_frame(&mut self, device: &mut Device, frame: &mut Frame) {
-        self.data32_texture.init(device, &mut frame.gpu_data32);
         self.layer_texture.init(device, &mut frame.layer_texture_data);
         self.render_task_texture.init(device, &mut frame.render_task_data);
 
         device.bind_texture(TextureSampler::Layers, self.layer_texture.id);
         device.bind_texture(TextureSampler::RenderTasks, self.render_task_texture.id);
-        device.bind_texture(TextureSampler::Data32, self.data32_texture.id);
     }
 }
 
 #[derive(Clone, Debug, PartialEq)]
 pub enum ReadPixelsFormat {
     Rgba8,
     Bgra8,
 }
@@ -1049,17 +1117,17 @@ impl Renderer {
         let default_font_render_mode = match (options.enable_aa, options.enable_subpixel_aa) {
             (true, true) => FontRenderMode::Subpixel,
             (true, false) => FontRenderMode::Alpha,
             (false, _) => FontRenderMode::Mono,
         };
 
         let config = FrameBuilderConfig {
             enable_scrollbars: options.enable_scrollbars,
-            default_font_render_mode: default_font_render_mode,
+            default_font_render_mode,
             debug: options.debug,
             cache_expiry_frames: options.cache_expiry_frames,
         };
 
         let device_pixel_ratio = options.device_pixel_ratio;
         let render_target_debug = options.render_target_debug;
         let payload_tx_for_backend = payload_tx.clone();
         let recorder = options.recorder;
@@ -1090,77 +1158,77 @@ impl Renderer {
             backend.run(backend_profile_counters);
         })};
 
         let gpu_cache_texture = CacheTexture::new(&mut device);
 
         let gpu_profile = GpuProfiler::new(device.rc_gl());
 
         let renderer = Renderer {
-            result_rx: result_rx,
-            device: device,
+            result_rx,
+            device,
             current_frame: None,
             pending_texture_updates: Vec::new(),
             pending_gpu_cache_updates: Vec::new(),
             pending_shader_updates: Vec::new(),
-            cs_box_shadow: cs_box_shadow,
-            cs_text_run: cs_text_run,
-            cs_blur: cs_blur,
-            cs_clip_rectangle: cs_clip_rectangle,
-            cs_clip_border: cs_clip_border,
-            cs_clip_image: cs_clip_image,
-            ps_rectangle: ps_rectangle,
-            ps_rectangle_clip: ps_rectangle_clip,
-            ps_text_run: ps_text_run,
-            ps_text_run_subpixel: ps_text_run_subpixel,
-            ps_image: ps_image,
-            ps_yuv_image: ps_yuv_image,
-            ps_border_corner: ps_border_corner,
-            ps_border_edge: ps_border_edge,
-            ps_box_shadow: ps_box_shadow,
-            ps_gradient: ps_gradient,
-            ps_angle_gradient: ps_angle_gradient,
-            ps_radial_gradient: ps_radial_gradient,
-            ps_cache_image: ps_cache_image,
-            ps_blend: ps_blend,
-            ps_hw_composite: ps_hw_composite,
-            ps_split_composite: ps_split_composite,
-            ps_composite: ps_composite,
-            notifier: notifier,
+            cs_box_shadow,
+            cs_text_run,
+            cs_blur,
+            cs_clip_rectangle,
+            cs_clip_border,
+            cs_clip_image,
+            ps_rectangle,
+            ps_rectangle_clip,
+            ps_text_run,
+            ps_text_run_subpixel,
+            ps_image,
+            ps_yuv_image,
+            ps_border_corner,
+            ps_border_edge,
+            ps_box_shadow,
+            ps_gradient,
+            ps_angle_gradient,
+            ps_radial_gradient,
+            ps_cache_image,
+            ps_blend,
+            ps_hw_composite,
+            ps_split_composite,
+            ps_composite,
+            notifier,
             debug: debug_renderer,
-            render_target_debug: render_target_debug,
+            render_target_debug,
             enable_batcher: options.enable_batcher,
             backend_profile_counters: BackendProfileCounters::new(),
             profile_counters: RendererProfileCounters::new(),
             profiler: Profiler::new(),
             enable_profiler: options.enable_profiler,
             max_recorded_profiles: options.max_recorded_profiles,
             clear_framebuffer: options.clear_framebuffer,
             clear_color: options.clear_color,
             enable_clear_scissor: options.enable_clear_scissor,
             last_time: 0,
             color_render_targets: Vec::new(),
             alpha_render_targets: Vec::new(),
-            gpu_profile: gpu_profile,
-            prim_vao_id: prim_vao_id,
-            blur_vao_id: blur_vao_id,
-            clip_vao_id: clip_vao_id,
+            gpu_profile,
+            prim_vao_id,
+            blur_vao_id,
+            clip_vao_id,
             gdt_index: 0,
-            gpu_data_textures: gpu_data_textures,
+            gpu_data_textures,
             pipeline_epoch_map: HashMap::default(),
-            main_thread_dispatcher: main_thread_dispatcher,
+            main_thread_dispatcher,
             cache_texture_id_map: Vec::new(),
-            dummy_cache_texture_id: dummy_cache_texture_id,
-            dither_matrix_texture_id: dither_matrix_texture_id,
+            dummy_cache_texture_id,
+            dither_matrix_texture_id,
             external_image_handler: None,
             external_images: HashMap::default(),
             vr_compositor_handler: vr_compositor,
             cpu_profiles: VecDeque::new(),
             gpu_profiles: VecDeque::new(),
-            gpu_cache_texture: gpu_cache_texture,
+            gpu_cache_texture,
         };
 
         let sender = RenderApiSender::new(api_tx, payload_tx);
         Ok((renderer, sender))
     }
 
     pub fn get_graphics_api_info(&self) -> GraphicsApiInfo {
         GraphicsApiInfo {
@@ -1277,17 +1345,17 @@ impl Renderer {
         let cpu_profiles = self.cpu_profiles.drain(..).collect();
         let gpu_profiles = self.gpu_profiles.drain(..).collect();
         (cpu_profiles, gpu_profiles)
     }
 
     /// Renders the current frame.
     ///
     /// A Frame is supplied by calling [`set_display_list()`][newframe].
-    /// [newframe]: ../../webrender_traits/struct.RenderApi.html#method.set_display_list
+    /// [newframe]: ../../webrender_api/struct.RenderApi.html#method.set_display_list
     pub fn render(&mut self, framebuffer_size: DeviceUintSize) {
         profile_scope!("render");
 
         if let Some(mut frame) = self.current_frame.take() {
             if let Some(ref mut frame) = frame.frame {
                 let mut profile_timers = RendererProfileTimers::new();
 
                 {
@@ -1314,20 +1382,19 @@ impl Renderer {
 
                         self.device.disable_scissor();
                         self.device.disable_depth();
                         self.device.set_blend(false);
                         //self.update_shaders();
 
                         self.update_texture_cache();
 
-                        self.update_gpu_cache();
-                        self.update_deferred_resolves(frame);
+                        self.update_gpu_cache(frame);
 
-                        self.device.bind_texture(TextureSampler::ResourceCache, self.gpu_cache_texture.current_id);
+                        self.device.bind_texture(TextureSampler::ResourceCache, self.gpu_cache_texture.texture_id);
 
                         frame_id
                     };
 
                     self.draw_tile_frame(frame, &framebuffer_size);
 
                     self.gpu_profile.end_frame();
                     cpu_frame_id
@@ -1391,21 +1458,23 @@ impl Renderer {
         }
 
         if update_uniforms {
             self.update_uniform_locations();
         }
     }
 */
 
-    fn update_gpu_cache(&mut self) {
+    fn update_gpu_cache(&mut self, frame: &mut Frame) {
         let _gm = GpuMarker::new(self.device.rc_gl(), "gpu cache update");
         for update_list in self.pending_gpu_cache_updates.drain(..) {
             self.gpu_cache_texture.update(&mut self.device, &update_list);
         }
+        self.update_deferred_resolves(frame);
+        self.gpu_cache_texture.flush(&mut self.device);
     }
 
     fn update_texture_cache(&mut self) {
         let _gm = GpuMarker::new(self.device.rc_gl(), "texture cache update");
         let mut pending_texture_updates = mem::replace(&mut self.pending_texture_updates, vec![]);
         for update_list in pending_texture_updates.drain(..) {
             for update in update_list.updates {
                 match update.op {
@@ -1754,26 +1823,31 @@ impl Renderer {
         //           blur radii with fixed weights.
         if !target.vertical_blurs.is_empty() || !target.horizontal_blurs.is_empty() {
             let _gm = self.gpu_profile.add_marker(GPU_TAG_BLUR);
             let vao = self.blur_vao_id;
 
             self.device.set_blend(false);
             let shader = self.cs_blur.get(&mut self.device).unwrap();
 
-            self.draw_instanced_batch(&target.vertical_blurs,
-                                      vao,
-                                      shader,
-                                      &BatchTextures::no_texture(),
-                                      &projection);
-            self.draw_instanced_batch(&target.horizontal_blurs,
-                                      vao,
-                                      shader,
-                                      &BatchTextures::no_texture(),
-                                      &projection);
+            if !target.vertical_blurs.is_empty() {
+                self.draw_instanced_batch(&target.vertical_blurs,
+                                          vao,
+                                          shader,
+                                          &BatchTextures::no_texture(),
+                                          &projection);
+            }
+
+            if !target.horizontal_blurs.is_empty() {
+                self.draw_instanced_batch(&target.horizontal_blurs,
+                                          vao,
+                                          shader,
+                                          &BatchTextures::no_texture(),
+                                          &projection);
+            }
         }
 
         // Draw any box-shadow caches for this target.
         if !target.box_shadow_cache_prims.is_empty() {
             self.device.set_blend(false);
             let _gm = self.gpu_profile.add_marker(GPU_TAG_CACHE_BOX_SHADOW);
             let vao = self.prim_vao_id;
             let shader = self.cs_box_shadow.get(&mut self.device).unwrap();
@@ -1800,68 +1874,76 @@ impl Renderer {
 
             self.draw_instanced_batch(&target.text_run_cache_prims,
                                       vao,
                                       shader,
                                       &target.text_run_textures,
                                       &projection);
         }
 
-        let _gm2 = GpuMarker::new(self.device.rc_gl(), "alpha batches");
-        self.device.set_blend(false);
-        let mut prev_blend_mode = BlendMode::None;
-
-        //Note: depth equality is needed for split planes
-        self.device.set_depth_func(DepthFunction::LessEqual);
-        self.device.enable_depth();
-        self.device.enable_depth_write();
+        if !target.alpha_batcher.is_empty() {
+            let _gm2 = GpuMarker::new(self.device.rc_gl(), "alpha batches");
+            self.device.set_blend(false);
+            let mut prev_blend_mode = BlendMode::None;
 
-        for batch in &target.alpha_batcher.batch_list.opaque_batches {
-            self.submit_batch(batch,
-                              &projection,
-                              render_task_data,
-                              color_cache_texture,
-                              render_target,
-                              target_size);
-        }
-
-        self.device.disable_depth_write();
+            //Note: depth equality is needed for split planes
+            self.device.set_depth_func(DepthFunction::LessEqual);
+            self.device.enable_depth();
+            self.device.enable_depth_write();
 
-        for batch in &target.alpha_batcher.batch_list.alpha_batches {
-            if batch.key.blend_mode != prev_blend_mode {
-                match batch.key.blend_mode {
-                    BlendMode::None => {
-                        self.device.set_blend(false);
-                    }
-                    BlendMode::Alpha => {
-                        self.device.set_blend(true);
-                        self.device.set_blend_mode_alpha();
-                    }
-                    BlendMode::PremultipliedAlpha => {
-                        self.device.set_blend(true);
-                        self.device.set_blend_mode_premultiplied_alpha();
-                    }
-                    BlendMode::Subpixel(color) => {
-                        self.device.set_blend(true);
-                        self.device.set_blend_mode_subpixel(color);
-                    }
-                }
-                prev_blend_mode = batch.key.blend_mode;
+            // Draw opaque batches front-to-back for maximum
+            // z-buffer efficiency!
+            for batch in target.alpha_batcher
+                               .batch_list
+                               .opaque_batches
+                               .iter()
+                               .rev() {
+                self.submit_batch(batch,
+                                  &projection,
+                                  render_task_data,
+                                  color_cache_texture,
+                                  render_target,
+                                  target_size);
             }
 
-            self.submit_batch(batch,
-                              &projection,
-                              render_task_data,
-                              color_cache_texture,
-                              render_target,
-                              target_size);
+            self.device.disable_depth_write();
+
+            for batch in &target.alpha_batcher.batch_list.alpha_batches {
+                if batch.key.blend_mode != prev_blend_mode {
+                    match batch.key.blend_mode {
+                        BlendMode::None => {
+                            self.device.set_blend(false);
+                        }
+                        BlendMode::Alpha => {
+                            self.device.set_blend(true);
+                            self.device.set_blend_mode_alpha();
+                        }
+                        BlendMode::PremultipliedAlpha => {
+                            self.device.set_blend(true);
+                            self.device.set_blend_mode_premultiplied_alpha();
+                        }
+                        BlendMode::Subpixel(color) => {
+                            self.device.set_blend(true);
+                            self.device.set_blend_mode_subpixel(color);
+                        }
+                    }
+                    prev_blend_mode = batch.key.blend_mode;
+                }
+
+                self.submit_batch(batch,
+                                  &projection,
+                                  render_task_data,
+                                  color_cache_texture,
+                                  render_target,
+                                  target_size);
+            }
+
+            self.device.disable_depth();
+            self.device.set_blend(false);
         }
-
-        self.device.disable_depth();
-        self.device.set_blend(false);
     }
 
     fn draw_alpha_target(&mut self,
                          render_target: (TextureId, i32),
                          target: &AlphaRenderTarget,
                          target_size: DeviceUintSize,
                          projection: &Transform3D<f32>) {
         {
@@ -1985,17 +2067,17 @@ impl Renderer {
                 self.external_images.insert((ext_image.id, ext_image.channel_index), texture_id);
 
                 let update = GpuCacheUpdate::Copy {
                     block_index: 0,
                     block_count: 1,
                     address: deferred_resolve.address,
                 };
                 let blocks = [ [image.u0, image.v0, image.u1, image.v1].into() ];
-                self.gpu_cache_texture.apply_patch(&mut self.device, &update, &blocks);
+                self.gpu_cache_texture.apply_patch(&update, &blocks);
             }
         }
     }
 
     fn unlock_external_images(&mut self) {
         if !self.external_images.is_empty() {
             let handler = self.external_image_handler
                               .as_mut()
--- a/gfx/webrender/src/resource_cache.rs
+++ b/gfx/webrender/src/resource_cache.rs
@@ -12,23 +12,23 @@ use profiler::TextureCacheProfileCounter
 use std::collections::{HashMap, HashSet};
 use std::collections::hash_map::Entry::{self, Occupied, Vacant};
 use std::fmt::Debug;
 use std::hash::BuildHasherDefault;
 use std::hash::Hash;
 use std::mem;
 use std::sync::Arc;
 use texture_cache::{TextureCache, TextureCacheItemId};
-use webrender_traits::{Epoch, FontKey, FontTemplate, GlyphKey, ImageKey, ImageRendering};
-use webrender_traits::{FontRenderMode, ImageData, GlyphDimensions, WebGLContextId};
-use webrender_traits::{DevicePoint, DeviceIntSize, DeviceUintRect, ImageDescriptor, ColorF};
-use webrender_traits::{GlyphOptions, GlyphInstance, TileOffset, TileSize};
-use webrender_traits::{BlobImageRenderer, BlobImageDescriptor, BlobImageError, BlobImageRequest, BlobImageData};
-use webrender_traits::BlobImageResources;
-use webrender_traits::{ExternalImageData, ExternalImageType, LayoutPoint};
+use api::{Epoch, FontKey, FontTemplate, GlyphKey, ImageKey, ImageRendering};
+use api::{FontRenderMode, ImageData, GlyphDimensions, WebGLContextId};
+use api::{DevicePoint, DeviceIntSize, DeviceUintRect, ImageDescriptor, ColorF};
+use api::{GlyphOptions, GlyphInstance, TileOffset, TileSize};
+use api::{BlobImageRenderer, BlobImageDescriptor, BlobImageError, BlobImageRequest, BlobImageData};
+use api::BlobImageResources;
+use api::{ExternalImageData, ExternalImageType, LayoutPoint};
 use rayon::ThreadPool;
 use glyph_rasterizer::{GlyphRasterizer, GlyphCache, GlyphRequest};
 
 const DEFAULT_TILE_SIZE: TileSize = 512;
 
 // These coordinates are always in texels.
 // They are converted to normalized ST
 // values in the vertex shader. The reason
@@ -223,23 +223,23 @@ impl ResourceCache {
             cached_glyphs: ResourceClassCache::new(),
             cached_images: ResourceClassCache::new(),
             webgl_textures: HashMap::default(),
             resources: Resources {
                 font_templates: HashMap::default(),
                 image_templates: ImageTemplates::new(),
             },
             cached_glyph_dimensions: HashMap::default(),
-            texture_cache: texture_cache,
+            texture_cache,
             state: State::Idle,
             current_frame_id: FrameId(0),
             pending_image_requests: Vec::new(),
             glyph_rasterizer: GlyphRasterizer::new(workers),
 
-            blob_image_renderer: blob_image_renderer,
+            blob_image_renderer,
             blob_image_requests: HashSet::new(),
 
             requested_glyphs: HashSet::default(),
             requested_images: HashSet::default(),
         }
     }
 
     pub fn max_texture_size(&self) -> u32 {
@@ -289,20 +289,20 @@ impl ResourceCache {
             self.blob_image_renderer.as_mut().unwrap().add(
                 image_key,
                 mem::replace(blob, BlobImageData::new()),
                 tiling
             );
         }
 
         let resource = ImageResource {
-            descriptor: descriptor,
-            data: data,
+            descriptor,
+            data,
             epoch: Epoch(0),
-            tiling: tiling,
+            tiling,
             dirty_rect: None,
         };
 
         self.resources.image_templates.insert(image_key, resource);
     }
 
     pub fn update_image_template(&mut self,
                                  image_key: ImageKey,
@@ -324,20 +324,20 @@ impl ResourceCache {
             if let ImageData::Blob(ref mut blob) = data {
                 self.blob_image_renderer.as_mut().unwrap().update(
                     image_key,
                     mem::replace(blob, BlobImageData::new())
                 );
             }
 
             ImageResource {
-                descriptor: descriptor,
-                data: data,
+                descriptor,
+                data,
                 epoch: next_epoch,
-                tiling: tiling,
+                tiling,
                 dirty_rect: match (dirty_rect, image.dirty_rect) {
                     (Some(rect), Some(prev_rect)) => Some(rect.union(&prev_rect)),
                     (Some(rect), None) => Some(rect),
                     _ => None,
                 },
             }
         } else {
             panic!("Attempt to update non-existant image (key {:?}).", image_key);
@@ -359,17 +359,17 @@ impl ResourceCache {
                 println!("Delete the non-exist key:{:?}", image_key);
             }
         }
     }
 
     pub fn add_webgl_texture(&mut self, id: WebGLContextId, texture_id: SourceTexture, size: DeviceIntSize) {
         self.webgl_textures.insert(id, WebGLTexture {
             id: texture_id,
-            size: size,
+            size,
         });
     }
 
     pub fn update_webgl_texture(&mut self, id: WebGLContextId, texture_id: SourceTexture, size: DeviceIntSize) {
         let webgl_texture = self.webgl_textures.get_mut(&id).unwrap();
 
         // Update new texture id and size
         webgl_texture.id = texture_id;
@@ -378,19 +378,19 @@ impl ResourceCache {
 
     pub fn request_image(&mut self,
                          key: ImageKey,
                          rendering: ImageRendering,
                          tile: Option<TileOffset>) {
 
         debug_assert_eq!(self.state, State::AddResources);
         let request = ImageRequest {
-            key: key,
-            rendering: rendering,
-            tile: tile,
+            key,
+            rendering,
+            tile,
         };
 
         let template = self.resources.image_templates.get(key).unwrap();
         if template.data.uses_texture_cache() {
             self.cached_images.mark_as_needed(&request, self.current_frame_id);
         }
         if template.data.is_blob() {
             if let Some(ref mut renderer) = self.blob_image_renderer {
@@ -428,17 +428,17 @@ impl ResourceCache {
                     };
 
                     renderer.request(
                         &self.resources,
                         request.into(),
                         &BlobImageDescriptor {
                             width: w,
                             height: h,
-                            offset: offset,
+                            offset,
                             format: template.descriptor.format,
                         },
                         template.dirty_rect,
                     );
                 }
             }
         } else {
             self.pending_image_requests.push(request);
@@ -520,17 +520,17 @@ impl ResourceCache {
     pub fn get_cached_image(&self,
                             image_key: ImageKey,
                             image_rendering: ImageRendering,
                             tile: Option<TileOffset>) -> CacheItem {
         debug_assert_eq!(self.state, State::QueryResources);
         let key = ImageRequest {
             key: image_key,
             rendering: image_rendering,
-            tile: tile,
+            tile,
         };
         let image_info = &self.cached_images.get(&key, self.current_frame_id);
         let item = self.texture_cache.get(image_info.texture_cache_id);
         CacheItem {
             texture_id: SourceTexture::TextureCache(item.texture_id),
             uv_rect_handle: item.uv_rect_handle,
         }
     }
@@ -551,17 +551,17 @@ impl ResourceCache {
                 }
             },
             // raw and blob image are all using resource_cache.
             ImageData::Raw(..) | ImageData::Blob(..) => None,
         };
 
         ImageProperties {
             descriptor: image_template.descriptor,
-            external_image: external_image,
+            external_image,
             tiling: image_template.tiling,
         }
     }
 
     pub fn get_webgl_texture(&self, context_id: &WebGLContextId) -> &WebGLTexture {
         &self.webgl_textures[context_id]
     }
 
@@ -630,25 +630,25 @@ impl ResourceCache {
                     }
                 }
             }
         }
 
         for texture_cache_item_id in self.requested_images.drain() {
             let item = self.texture_cache.get_mut(texture_cache_item_id);
             if let Some(mut request) = gpu_cache.request(&mut item.uv_rect_handle) {
-                request.push(item.uv_rect.into());
+                request.push(item.uv_rect);
             }
         }
 
         for texture_cache_item_id in self.requested_glyphs.drain() {
             let item = self.texture_cache.get_mut(texture_cache_item_id);
             if let Some(mut request) = gpu_cache.request(&mut item.uv_rect_handle) {
-                request.push(item.uv_rect.into());
-                request.push([item.user_data[0], item.user_data[1], 0.0, 0.0].into());
+                request.push(item.uv_rect);
+                request.push([item.user_data[0], item.user_data[1], 0.0, 0.0]);
             }
         }
     }
 
     fn update_texture_cache(&mut self,
                             request: &ImageRequest,
                             image_data: Option<ImageData>,
                             texture_cache_profile: &mut TextureCacheProfileCounters) {
@@ -676,18 +676,18 @@ impl ResourceCache {
                 let offset = image_descriptor.offset + tile.y as u32 * tile_size as u32 * stride
                                                      + tile.x as u32 * tile_size as u32 * bpp;
                 (Some(stride), offset)
             };
 
             ImageDescriptor {
                 width: actual_width,
                 height: actual_height,
-                stride: stride,
-                offset: offset,
+                stride,
+                offset,
                 format: image_descriptor.format,
                 is_opaque: image_descriptor.is_opaque,
             }
         } else {
             image_template.descriptor.clone()
         };
 
         let image_id = match self.cached_images.entry(*request, self.current_frame_id) {
--- a/gfx/webrender/src/scene.rs
+++ b/gfx/webrender/src/scene.rs
@@ -1,17 +1,17 @@
 /* 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 fnv::FnvHasher;
 use std::collections::HashMap;
 use std::hash::BuildHasherDefault;
-use webrender_traits::{BuiltDisplayList, ColorF, DynamicProperties, Epoch, LayerSize, LayoutSize};
-use webrender_traits::{LayoutTransform, PipelineId, PropertyBinding, PropertyBindingId};
+use api::{BuiltDisplayList, ColorF, DynamicProperties, Epoch, LayerSize, LayoutSize};
+use api::{LayoutTransform, PipelineId, PropertyBinding, PropertyBindingId};
 
 /// Stores a map of the animated property bindings for the current display list. These
 /// can be used to animate the transform and/or opacity of a display list without
 /// re-submitting the display list itself.
 pub struct SceneProperties {
     transform_properties: HashMap<PropertyBindingId, LayoutTransform>,
     float_properties: HashMap<PropertyBindingId, f32>,
 }
@@ -115,18 +115,18 @@ impl Scene {
                             epoch: Epoch,
                             built_display_list: BuiltDisplayList,
                             background_color: Option<ColorF>,
                             viewport_size: LayerSize,
                             content_size: LayoutSize) {
         self.display_lists.insert(pipeline_id, built_display_list);
 
         let new_pipeline = ScenePipeline {
-            pipeline_id: pipeline_id,
-            epoch: epoch,
-            viewport_size: viewport_size,
-            content_size: content_size,
-            background_color: background_color,
+            pipeline_id,
+            epoch,
+            viewport_size,
+            content_size,
+            background_color,
         };
 
         self.pipeline_map.insert(pipeline_id, new_pipeline);
     }
 }
--- a/gfx/webrender/src/spring.rs
+++ b/gfx/webrender/src/spring.rs
@@ -1,13 +1,13 @@
 /* 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_traits::LayerPoint;
+use api::LayerPoint;
 
 /// Some arbitrarily small positive number used as threshold value.
 pub const EPSILON: f32 = 0.1;
 
 /// The default stiffness factor.
 pub const STIFFNESS: f32 = 0.2;
 
 /// The default damping factor.
@@ -29,18 +29,18 @@ pub struct Spring {
 
 impl Spring {
     /// Create a new spring at location.
     pub fn at(pos: LayerPoint, stiffness: f32, damping: f32) -> Spring {
         Spring {
             cur: pos,
             prev: pos,
             dest: pos,
-            stiffness: stiffness,
-            damping: damping,
+            stiffness,
+            damping,
         }
     }
 
     /// Set coords on a spring, mutating spring
     pub fn coords(&mut self, cur: LayerPoint, prev: LayerPoint, dest: LayerPoint) {
         self.cur = cur;
         self.prev = prev;
         self.dest = dest
--- a/gfx/webrender/src/texture_cache.rs
+++ b/gfx/webrender/src/texture_cache.rs
@@ -12,19 +12,19 @@ use profiler::TextureCacheProfileCounter
 use std::cmp;
 use std::collections::HashMap;
 use std::collections::hash_map::Entry;
 use std::hash::BuildHasherDefault;
 use std::mem;
 use std::slice::Iter;
 use time;
 use util;
-use webrender_traits::{ExternalImageType, ImageData, ImageFormat};
-use webrender_traits::{DeviceUintRect, DeviceUintSize, DeviceUintPoint};
-use webrender_traits::{DevicePoint, ImageDescriptor};
+use api::{ExternalImageType, ImageData, ImageFormat};
+use api::{DeviceUintRect, DeviceUintSize, DeviceUintPoint};
+use api::{DevicePoint, ImageDescriptor};
 
 /// The number of bytes we're allowed to use for a texture.
 const MAX_BYTES_PER_TEXTURE: u32 = 1024 * 1024 * 256;  // 256MB
 
 /// The number of RGBA pixels we're allowed to use for a texture.
 const MAX_RGBA_PIXELS_PER_TEXTURE: u32 = MAX_BYTES_PER_TEXTURE / 4;
 
 /// The desired initial size of each texture, in pixels.
@@ -72,18 +72,18 @@ pub struct TexturePage {
     coalesce_vec: Vec<DeviceUintRect>,
     allocations: u32,
     dirty: bool,
 }
 
 impl TexturePage {
     pub fn new(texture_id: CacheTextureId, texture_size: DeviceUintSize) -> TexturePage {
         let mut page = TexturePage {
-            texture_id: texture_id,
-            texture_size: texture_size,
+            texture_id,
+            texture_size,
             free_list: FreeRectList::new(),
             coalesce_vec: Vec::new(),
             allocations: 0,
             dirty: false,
         };
         page.clear();
         page
     }
@@ -491,26 +491,26 @@ impl FreeListItem for TextureCacheItem {
 }
 
 impl TextureCacheItem {
     fn new(texture_id: CacheTextureId,
            rect: DeviceUintRect,
            user_data: [f32; 2])
            -> TextureCacheItem {
         TextureCacheItem {
-            texture_id: texture_id,
+            texture_id,
             uv_rect: UvRect {
                 uv0: DevicePoint::new(rect.origin.x as f32,
                                       rect.origin.y as f32),
                 uv1: DevicePoint::new((rect.origin.x + rect.size.width) as f32,
                                       (rect.origin.y + rect.size.height) as f32),
             },
             allocated_rect: rect,
             uv_rect_handle: GpuCacheHandle::new(),
-            user_data: user_data,
+            user_data,
         }
     }
 }
 
 struct TextureCacheArena {
     pages_a8: Vec<TexturePage>,
     pages_rgb8: Vec<TexturePage>,
     pages_rgba8: Vec<TexturePage>,
@@ -598,17 +598,17 @@ impl TextureCache {
         }
 
         TextureCache {
             cache_id_list: CacheTextureIdList::new(),
             free_texture_levels: HashMap::default(),
             items: FreeList::new(),
             pending_updates: TextureUpdateList::new(),
             arena: TextureCacheArena::new(),
-            max_texture_size: max_texture_size,
+            max_texture_size,
         }
     }
 
     pub fn max_texture_size(&self) -> u32 {
         self.max_texture_size
     }
 
     pub fn pending_updates(&mut self) -> TextureUpdateList {
@@ -637,17 +637,17 @@ impl TextureCache {
                 texture_id,
                 DeviceUintRect::new(DeviceUintPoint::zero(), requested_size),
                 user_data);
             let image_id = self.items.insert(cache_item);
 
             return AllocationResult {
                 item: self.items.get(image_id).clone(),
                 kind: AllocationKind::Standalone,
-                image_id: image_id,
+                image_id,
             }
         }
 
         let mode = RenderTargetMode::SimpleRenderTarget;
         let (page_list, page_profile) = match format {
             ImageFormat::A8 => (&mut self.arena.pages_a8, &mut profile.pages_a8),
             ImageFormat::BGRA8 => (&mut self.arena.pages_rgba8, &mut profile.pages_rgba8),
             ImageFormat::RGB8 => (&mut self.arena.pages_rgb8, &mut profile.pages_rgb8),
@@ -712,17 +712,17 @@ impl TextureCache {
 
                     let update_op = TextureUpdate {
                         id: texture_id,
                         op: texture_create_op(texture_size, format, mode),
                     };
                     self.pending_updates.push(update_op);
 
                     free_texture_levels.push(FreeTextureLevel {
-                        texture_id: texture_id,
+                        texture_id,
                     });
                 }
                 let free_texture_level = free_texture_levels.pop().unwrap();
                 let texture_id = free_texture_level.texture_id;
 
                 let page = TexturePage::new(texture_id, texture_size);
                 page_list.push(page);
                 page_list.last_mut().unwrap()
@@ -734,17 +734,17 @@ impl TextureCache {
         let cache_item = TextureCacheItem::new(page.texture_id,
                                                DeviceUintRect::new(location, requested_size),
                                                user_data);
         let image_id = self.items.insert(cache_item.clone());
 
         AllocationResult {
             item: cache_item,
             kind: AllocationKind::TexturePage,
-            image_id: image_id,
+            image_id,
         }
     }
 
     pub fn update(&mut self,
                   image_id: TextureCacheItemId,
                   descriptor: ImageDescriptor,
                   data: ImageData,
                   dirty_rect: Option<DeviceUintRect>) {
@@ -768,17 +768,17 @@ impl TextureCache {
                         let offset = descriptor.offset + dirty.origin.y * stride + dirty.origin.x;
                         TextureUpdateOp::Update {
                             page_pos_x: existing_item.allocated_rect.origin.x + dirty.origin.x,
                             page_pos_y: existing_item.allocated_rect.origin.y + dirty.origin.y,
                             width: dirty.size.width,
                             height: dirty.size.height,
                             data: bytes,
                             stride: Some(stride),
-                            offset: offset,
+                            offset,
                         }
                     }
                     None => {
                         TextureUpdateOp::Update {
                             page_pos_x: existing_item.allocated_rect.origin.x,
                             page_pos_y: existing_item.allocated_rect.origin.y,
                             width: descriptor.width,
                             height: descriptor.height,
@@ -788,17 +788,17 @@ impl TextureCache {
                         }
                     }
                 }
             }
         };
 
         let update_op = TextureUpdate {
             id: existing_item.texture_id,
-            op: op,
+            op,
         };
 
         self.pending_updates.push(update_op);
     }
 
     pub fn insert(&mut self,
                   descriptor: ImageDescriptor,
                   filter: TextureFilter,
@@ -840,17 +840,17 @@ impl TextureCache {
                             }
                             ExternalImageType::ExternalBuffer => {
                                 let update_op = TextureUpdate {
                                     id: result.item.texture_id,
                                     op: TextureUpdateOp::UpdateForExternalBuffer {
                                         rect: result.item.allocated_rect,
                                         id: ext_image.id,
                                         channel_index: ext_image.channel_index,
-                                        stride: stride,
+                                        stride,
                                         offset: descriptor.offset,
                                     },
                                 };
 
                                 self.pending_updates.push(update_op);
                             }
                         }
                     }
@@ -861,17 +861,17 @@ impl TextureCache {
                         let update_op = TextureUpdate {
                             id: result.item.texture_id,
                             op: TextureUpdateOp::Update {
                                 page_pos_x: result.item.allocated_rect.origin.x,
                                 page_pos_y: result.item.allocated_rect.origin.y,
                                 width: result.item.allocated_rect.size.width,
                                 height: result.item.allocated_rect.size.height,
                                 data: bytes,
-                                stride: stride,
+                                stride,
                                 offset: descriptor.offset,
                             },
                         };
 
                         self.pending_updates.push(update_op);
                     }
                 }
             }
@@ -883,37 +883,37 @@ impl TextureCache {
                             ExternalImageType::TextureRectHandle |
                             ExternalImageType::TextureExternalHandle => {
                                 panic!("External texture handle should not go through texture_cache.");
                             }
                             ExternalImageType::ExternalBuffer => {
                                 let update_op = TextureUpdate {
                                     id: result.item.texture_id,
                                     op: TextureUpdateOp::Create {
-                                        width: width,
-                                        height: height,
-                                        format: format,
-                                        filter: filter,
+                                        width,
+                                        height,
+                                        format,
+                                        filter,
                                         mode: RenderTargetMode::None,
                                         data: Some(data),
                                     },
                                 };
 
                                 self.pending_updates.push(update_op);
                             }
                         }
                     }
                     _ => {
                         let update_op = TextureUpdate {
                             id: result.item.texture_id,
                             op: TextureUpdateOp::Create {
-                                width: width,
-                                height: height,
-                                format: format,
-                                filter: filter,
+                                width,
+                                height,
+                                format,
+                                filter,
                                 mode: RenderTargetMode::None,
                                 data: Some(data),
                             },
                         };
 
                         self.pending_updates.push(update_op);
                     }
                 }
@@ -948,33 +948,33 @@ impl TextureCache {
     }
 }
 
 fn texture_create_op(texture_size: DeviceUintSize, format: ImageFormat, mode: RenderTargetMode)
                      -> TextureUpdateOp {
     TextureUpdateOp::Create {
         width: texture_size.width,
         height: texture_size.height,
-        format: format,
+        format,
         filter: TextureFilter::Linear,
-        mode: mode,
+        mode,
         data: None,
     }
 }
 
 fn texture_grow_op(texture_size: DeviceUintSize,
                    format: ImageFormat,
                    mode: RenderTargetMode)
                    -> TextureUpdateOp {
     TextureUpdateOp::Grow {
         width: texture_size.width,
         height: texture_size.height,
-        format: format,
+        format,
         filter: TextureFilter::Linear,
-        mode: mode,
+        mode,
     }
 }
 
 trait FitsInside {
     fn fits_inside(&self, other: &Self) -> bool;
 }
 
 impl FitsInside for DeviceUintSize {
--- a/gfx/webrender/src/tiling.rs
+++ b/gfx/webrender/src/tiling.rs
@@ -2,40 +2,38 @@
  * 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 app_units::Au;
 use border::{BorderCornerInstance, BorderCornerSide};
 use device::TextureId;
 use fnv::FnvHasher;
 use gpu_cache::{GpuCache, GpuCacheHandle, GpuCacheUpdateList};
-use gpu_store::GpuStoreAddress;
 use internal_types::{ANGLE_FLOAT_TO_FIXED, BatchTextures, CacheTextureId, LowLevelFilterOp};
 use internal_types::SourceTexture;
 use mask_cache::MaskCacheInfo;
-use prim_store::{CLIP_DATA_GPU_SIZE, DeferredResolve, GpuBlock32};
-use prim_store::{ImagePrimitiveKind, PrimitiveCacheKey};
+use prim_store::{CLIP_DATA_GPU_BLOCKS, DeferredResolve, ImagePrimitiveKind, PrimitiveCacheKey};
 use prim_store::{PrimitiveIndex, PrimitiveKind, PrimitiveMetadata, PrimitiveStore};
 use profiler::FrameProfileCounters;
 use render_task::{AlphaRenderItem, MaskGeometryKind, MaskSegment, RenderTask, RenderTaskData};
 use render_task::{RenderTaskId, RenderTaskIndex, RenderTaskKey, RenderTaskKind};
 use render_task::RenderTaskLocation;
 use renderer::BlendMode;
 use renderer::ImageBufferKind;
 use resource_cache::ResourceCache;
 use std::{f32, i32, mem, usize};
 use std::collections::HashMap;
 use std::hash::BuildHasherDefault;
 use texture_cache::TexturePage;
 use util::{TransformedRect, TransformedRectKind};
-use webrender_traits::{BuiltDisplayList, ClipAndScrollInfo, ClipId, ColorF, DeviceIntPoint, ImageKey};
-use webrender_traits::{DeviceIntRect, DeviceIntSize, DeviceUintPoint, DeviceUintSize};
-use webrender_traits::{ExternalImageType, FontRenderMode, ImageRendering, LayerRect};
-use webrender_traits::{LayerToWorldTransform, MixBlendMode, PipelineId, TransformStyle};
-use webrender_traits::{TileOffset, WorldToLayerTransform, YuvColorSpace, YuvFormat, LayerVector2D};
+use api::{BuiltDisplayList, ClipAndScrollInfo, ClipId, ColorF, DeviceIntPoint, ImageKey};
+use api::{DeviceIntRect, DeviceIntSize, DeviceUintPoint, DeviceUintSize};
+use api::{ExternalImageType, FontRenderMode, ImageRendering, LayerRect};
+use api::{LayerToWorldTransform, MixBlendMode, PipelineId, TransformStyle};
+use api::{TileOffset, WorldToLayerTransform, YuvColorSpace, YuvFormat, LayerVector2D};
 
 // Special sentinel value recognized by the shader. It is considered to be
 // a dummy task that doesn't mask out anything.
 const OPAQUE_TASK_INDEX: RenderTaskIndex = RenderTaskIndex(i32::MAX as usize);
 
 
 pub type DisplayListMap = HashMap<PipelineId,
                                   BuiltDisplayList,
@@ -104,21 +102,23 @@ pub enum PrimitiveFlags {
 }