Splitting of layers for correct intersections

Sorting 3d-sorted layers and rendering them in that order causes issues
when layers intersect. Instead place 3d-sorted layers in a bsp tree and
fragment any intersecting layers into non-rectangular quads. We can then
render the fragments in the correct sorted order regardless of
intersections.

BUG=455918,159225,132122,230833

Review URL: https://codereview.chromium.org/595593002

Cr-Commit-Position: refs/heads/master@{#320276}
diff --git a/cc/BUILD.gn b/cc/BUILD.gn
index 85815417..bf77c0e 100644
--- a/cc/BUILD.gn
+++ b/cc/BUILD.gn
@@ -253,6 +253,8 @@
     "output/delegating_renderer.h",
     "output/direct_renderer.cc",
     "output/direct_renderer.h",
+    "output/dynamic_geometry_binding.cc",
+    "output/dynamic_geometry_binding.h",
     "output/filter_operation.cc",
     "output/filter_operation.h",
     "output/filter_operations.cc",
@@ -293,6 +295,8 @@
     "output/software_output_device.h",
     "output/software_renderer.cc",
     "output/software_renderer.h",
+    "output/static_geometry_binding.cc",
+    "output/static_geometry_binding.h",
     "output/viewport_selection_bound.cc",
     "output/viewport_selection_bound.h",
     "output/vsync_parameter_observer.h",
@@ -504,8 +508,6 @@
     "trees/damage_tracker.h",
     "trees/draw_property_utils.cc",
     "trees/draw_property_utils.h",
-    "trees/layer_sorter.cc",
-    "trees/layer_sorter.h",
     "trees/layer_tree_host.cc",
     "trees/layer_tree_host.h",
     "trees/layer_tree_host_client.h",
@@ -830,7 +832,6 @@
     "test/test_web_graphics_context_3d_unittest.cc",
     "trees/blocking_task_runner_unittest.cc",
     "trees/damage_tracker_unittest.cc",
-    "trees/layer_sorter_unittest.cc",
     "trees/layer_tree_host_common_unittest.cc",
     "trees/layer_tree_host_impl_unittest.cc",
     "trees/layer_tree_host_pixeltest_blending.cc",
diff --git a/cc/cc.gyp b/cc/cc.gyp
index c2a971b..bc83b1f 100644
--- a/cc/cc.gyp
+++ b/cc/cc.gyp
@@ -279,6 +279,8 @@
         'output/delegating_renderer.h',
         'output/direct_renderer.cc',
         'output/direct_renderer.h',
+        'output/dynamic_geometry_binding.cc',
+        'output/dynamic_geometry_binding.h',
         'output/filter_operation.cc',
         'output/filter_operation.h',
         'output/filter_operations.cc',
@@ -319,6 +321,8 @@
         'output/software_output_device.h',
         'output/software_renderer.cc',
         'output/software_renderer.h',
+        'output/static_geometry_binding.cc',
+        'output/static_geometry_binding.h',
         'output/viewport_selection_bound.cc',
         'output/viewport_selection_bound.h',
         'output/vsync_parameter_observer.h',
@@ -531,8 +535,6 @@
         'trees/damage_tracker.h',
         'trees/draw_property_utils.cc',
         'trees/draw_property_utils.h',
-        'trees/layer_sorter.cc',
-        'trees/layer_sorter.h',
         'trees/layer_tree_host.cc',
         'trees/layer_tree_host.h',
         'trees/layer_tree_host_client.h',
diff --git a/cc/cc_tests.gyp b/cc/cc_tests.gyp
index fd5de02..bafd7477 100644
--- a/cc/cc_tests.gyp
+++ b/cc/cc_tests.gyp
@@ -106,7 +106,6 @@
       'test/test_web_graphics_context_3d_unittest.cc',
       'trees/blocking_task_runner_unittest.cc',
       'trees/damage_tracker_unittest.cc',
-      'trees/layer_sorter_unittest.cc',
       'trees/layer_tree_host_common_unittest.cc',
       'trees/layer_tree_host_impl_unittest.cc',
       'trees/layer_tree_host_pixeltest_blending.cc',
diff --git a/cc/output/bsp_tree.cc b/cc/output/bsp_tree.cc
index f2318ce..4eb87cb 100644
--- a/cc/output/bsp_tree.cc
+++ b/cc/output/bsp_tree.cc
@@ -62,10 +62,9 @@
         scoped_ptr<DrawPolygon> polygon;
         scoped_ptr<DrawPolygon> new_front;
         scoped_ptr<DrawPolygon> new_back;
-        bool split_result = false;
         // Time to split this geometry, *it needs to be split by node_data.
         polygon = polygon_list->take_front();
-        split_result =
+        bool split_result =
             polygon->Split(*(node->node_data), &new_front, &new_back);
         DCHECK(split_result);
         if (!split_result) {
diff --git a/cc/output/bsp_walk_action.cc b/cc/output/bsp_walk_action.cc
index da5ada5a..9ffc299 100644
--- a/cc/output/bsp_walk_action.cc
+++ b/cc/output/bsp_walk_action.cc
@@ -9,15 +9,37 @@
 #include "base/memory/scoped_ptr.h"
 #include "cc/output/direct_renderer.h"
 #include "cc/quads/draw_polygon.h"
+#include "cc/quads/draw_quad.h"
 
 namespace cc {
 
-void BspWalkActionToVector::operator()(DrawPolygon* item) {
-  list_->push_back(item);
+BspWalkActionDrawPolygon::BspWalkActionDrawPolygon(
+    DirectRenderer* renderer,
+    DirectRenderer::DrawingFrame* frame,
+    const gfx::Rect& render_pass_scissor,
+    bool using_scissor_as_optimization)
+    : renderer_(renderer),
+      frame_(frame),
+      render_pass_scissor_(render_pass_scissor),
+      using_scissor_as_optimization_(using_scissor_as_optimization) {
+}
+
+void BspWalkActionDrawPolygon::operator()(DrawPolygon* item) {
+  gfx::Transform inverse_transform;
+  bool invertible =
+      item->original_ref()->quadTransform().GetInverse(&inverse_transform);
+  DCHECK(invertible);
+  item->TransformToLayerSpace(inverse_transform);
+  renderer_->DoDrawPolygon(*item, frame_, render_pass_scissor_,
+                           using_scissor_as_optimization_);
 }
 
 BspWalkActionToVector::BspWalkActionToVector(std::vector<DrawPolygon*>* in_list)
     : list_(in_list) {
 }
 
+void BspWalkActionToVector::operator()(DrawPolygon* item) {
+  list_->push_back(item);
+}
+
 }  // namespace cc
diff --git a/cc/output/bsp_walk_action.h b/cc/output/bsp_walk_action.h
index ac8fa41..a89d8ea 100644
--- a/cc/output/bsp_walk_action.h
+++ b/cc/output/bsp_walk_action.h
@@ -20,6 +20,22 @@
 
 // The BspTree class takes ownership of all the DrawPolygons returned in list_
 // so the BspTree must be preserved while elements in that vector are in use.
+class CC_EXPORT BspWalkActionDrawPolygon : public BspWalkAction {
+ public:
+  void operator()(DrawPolygon* item) override;
+
+  BspWalkActionDrawPolygon(DirectRenderer* renderer,
+                           DirectRenderer::DrawingFrame* frame,
+                           const gfx::Rect& render_pass_scissor,
+                           bool using_scissor_as_optimization);
+
+ private:
+  DirectRenderer* renderer_;
+  DirectRenderer::DrawingFrame* frame_;
+  const gfx::Rect& render_pass_scissor_;
+  bool using_scissor_as_optimization_;
+};
+
 class CC_EXPORT BspWalkActionToVector : public BspWalkAction {
  public:
   explicit BspWalkActionToVector(std::vector<DrawPolygon*>* in_list);
diff --git a/cc/output/direct_renderer.cc b/cc/output/direct_renderer.cc
index 5867fce..0ae4179d 100644
--- a/cc/output/direct_renderer.cc
+++ b/cc/output/direct_renderer.cc
@@ -12,6 +12,8 @@
 #include "base/metrics/histogram.h"
 #include "base/trace_event/trace_event.h"
 #include "cc/base/math_util.h"
+#include "cc/output/bsp_tree.h"
+#include "cc/output/bsp_walk_action.h"
 #include "cc/output/copy_output_request.h"
 #include "cc/quads/draw_quad.h"
 #include "ui/gfx/geometry/rect_conversions.h"
@@ -299,22 +301,27 @@
   EnsureScissorTestDisabled();
 }
 
+bool DirectRenderer::ShouldSkipQuad(const DrawQuad& quad,
+                                    const gfx::Rect& render_pass_scissor) {
+  if (render_pass_scissor.IsEmpty())
+    return true;
+
+  if (quad.isClipped()) {
+    gfx::Rect r = quad.clipRect();
+    r.Intersect(render_pass_scissor);
+    return r.IsEmpty();
+  }
+
+  return false;
+}
+
 void DirectRenderer::SetScissorStateForQuadWithRenderPassScissor(
     const DrawingFrame* frame,
     const DrawQuad& quad,
-    const gfx::Rect& render_pass_scissor,
-    bool* should_skip_quad) {
+    const gfx::Rect& render_pass_scissor) {
   gfx::Rect quad_scissor_rect = render_pass_scissor;
-
   if (quad.isClipped())
     quad_scissor_rect.Intersect(quad.clipRect());
-
-  if (quad_scissor_rect.IsEmpty()) {
-    *should_skip_quad = true;
-    return;
-  }
-
-  *should_skip_quad = false;
   SetScissorTestRectInDrawSpace(frame, quad_scissor_rect);
 }
 
@@ -330,6 +337,46 @@
 
 void DirectRenderer::FinishDrawingQuadList() {}
 
+void DirectRenderer::DoDrawPolygon(const DrawPolygon& poly,
+                                   DrawingFrame* frame,
+                                   const gfx::Rect& render_pass_scissor,
+                                   bool using_scissor_as_optimization) {
+  if (using_scissor_as_optimization) {
+    SetScissorStateForQuadWithRenderPassScissor(frame, *poly.original_ref(),
+                                                render_pass_scissor);
+  } else {
+    SetScissorStateForQuad(frame, *poly.original_ref());
+  }
+
+  // If the poly has not been split, then it is just a normal DrawQuad,
+  // and we should save any extra processing that would have to be done.
+  if (!poly.is_split()) {
+    DoDrawQuad(frame, poly.original_ref(), NULL);
+    return;
+  }
+
+  std::vector<gfx::QuadF> quads;
+  poly.ToQuads2D(&quads);
+  for (size_t i = 0; i < quads.size(); ++i) {
+    DoDrawQuad(frame, poly.original_ref(), &quads[i]);
+  }
+}
+
+void DirectRenderer::FlushPolygons(ScopedPtrDeque<DrawPolygon>* poly_list,
+                                   DrawingFrame* frame,
+                                   const gfx::Rect& render_pass_scissor,
+                                   bool using_scissor_as_optimization) {
+  if (poly_list->empty()) {
+    return;
+  }
+
+  BspTree bsp_tree(poly_list);
+  BspWalkActionDrawPolygon action_handler(this, frame, render_pass_scissor,
+                                          using_scissor_as_optimization);
+  bsp_tree.TraverseWithActionHandler(&action_handler);
+  DCHECK(poly_list->empty());
+}
+
 void DirectRenderer::DrawRenderPass(DrawingFrame* frame,
                                     const RenderPass* render_pass) {
   TRACE_EVENT0("cc", "DirectRenderer::DrawRenderPass");
@@ -369,21 +416,49 @@
   }
 
   const QuadList& quad_list = render_pass->quad_list;
+  ScopedPtrDeque<DrawPolygon> poly_list;
+
+  int next_polygon_id = 0;
+  int last_sorting_context_id = 0;
   for (auto it = quad_list.BackToFrontBegin(); it != quad_list.BackToFrontEnd();
        ++it) {
     const DrawQuad& quad = **it;
-    bool should_skip_quad = false;
+    gfx::QuadF send_quad(quad.visible_rect);
 
+    if (using_scissor_as_optimization &&
+        ShouldSkipQuad(quad, render_pass_scissor)) {
+      continue;
+    }
+
+    if (last_sorting_context_id != quad.shared_quad_state->sorting_context_id) {
+      last_sorting_context_id = quad.shared_quad_state->sorting_context_id;
+      FlushPolygons(&poly_list, frame, render_pass_scissor,
+                    using_scissor_as_optimization);
+    }
+
+    // This layer is in a 3D sorting context so we add it to the list of
+    // polygons to go into the BSP tree.
+    if (quad.shared_quad_state->sorting_context_id != 0) {
+      scoped_ptr<DrawPolygon> new_polygon(new DrawPolygon(
+          *it, quad.visible_rect, quad.quadTransform(), next_polygon_id++));
+      if (new_polygon->points().size() > 2u) {
+        poly_list.push_back(new_polygon.Pass());
+      }
+      continue;
+    }
+
+    // We are not in a 3d sorting context, so we should draw the quad normally.
     if (using_scissor_as_optimization) {
-      SetScissorStateForQuadWithRenderPassScissor(
-          frame, quad, render_pass_scissor, &should_skip_quad);
+      SetScissorStateForQuadWithRenderPassScissor(frame, quad,
+                                                  render_pass_scissor);
     } else {
       SetScissorStateForQuad(frame, quad);
     }
 
-    if (!should_skip_quad)
-      DoDrawQuad(frame, &quad);
+    DoDrawQuad(frame, &quad, nullptr);
   }
+  FlushPolygons(&poly_list, frame, render_pass_scissor,
+                using_scissor_as_optimization);
   FinishDrawingQuadList();
 }
 
diff --git a/cc/output/direct_renderer.h b/cc/output/direct_renderer.h
index e07cb93..3399491c 100644
--- a/cc/output/direct_renderer.h
+++ b/cc/output/direct_renderer.h
@@ -9,13 +9,17 @@
 #include "base/callback.h"
 #include "base/containers/scoped_ptr_hash_map.h"
 #include "cc/base/cc_export.h"
+#include "cc/base/scoped_ptr_deque.h"
 #include "cc/output/overlay_processor.h"
 #include "cc/output/renderer.h"
 #include "cc/resources/resource_provider.h"
 #include "cc/resources/scoped_resource.h"
+#include "cc/resources/task_graph_runner.h"
+#include "ui/gfx/geometry/quad_f.h"
 
 namespace cc {
 
+class DrawPolygon;
 class ResourceProvider;
 
 // This is the base class for code shared between the GL and software
@@ -56,6 +60,10 @@
   };
 
   void SetEnlargePassTextureAmountForTesting(const gfx::Vector2d& amount);
+  void DoDrawPolygon(const DrawPolygon& poly,
+                     DrawingFrame* frame,
+                     const gfx::Rect& render_pass_scissor,
+                     bool using_scissor_as_optimization);
 
  protected:
   DirectRenderer(RendererClient* client,
@@ -78,16 +86,21 @@
   gfx::Rect DeviceClipRectInWindowSpace(const DrawingFrame* frame) const;
   static gfx::Rect ComputeScissorRectForRenderPass(const DrawingFrame* frame);
   void SetScissorStateForQuad(const DrawingFrame* frame, const DrawQuad& quad);
+  bool ShouldSkipQuad(const DrawQuad& quad,
+                      const gfx::Rect& render_pass_scissor);
   void SetScissorStateForQuadWithRenderPassScissor(
       const DrawingFrame* frame,
       const DrawQuad& quad,
-      const gfx::Rect& render_pass_scissor,
-      bool* should_skip_quad);
+      const gfx::Rect& render_pass_scissor);
   void SetScissorTestRectInDrawSpace(const DrawingFrame* frame,
                                      const gfx::Rect& draw_space_rect);
 
   static gfx::Size RenderPassTextureSize(const RenderPass* render_pass);
 
+  void FlushPolygons(ScopedPtrDeque<DrawPolygon>* poly_list,
+                     DrawingFrame* frame,
+                     const gfx::Rect& render_pass_scissor,
+                     bool using_scissor_as_optimization);
   void DrawRenderPass(DrawingFrame* frame, const RenderPass* render_pass);
   bool UseRenderPass(DrawingFrame* frame, const RenderPass* render_pass);
 
@@ -101,7 +114,12 @@
                              bool draw_rect_covers_full_surface) = 0;
   virtual void ClearFramebuffer(DrawingFrame* frame,
                                 bool has_external_stencil_test) = 0;
-  virtual void DoDrawQuad(DrawingFrame* frame, const DrawQuad* quad) = 0;
+  // clip_region is a (possibly null) pointer to a quad in the same
+  // space as the quad. When non-null only the area of the quad that overlaps
+  // with clip_region will be drawn.
+  virtual void DoDrawQuad(DrawingFrame* frame,
+                          const DrawQuad* quad,
+                          const gfx::QuadF* clip_region) = 0;
   virtual void BeginDrawingFrame(DrawingFrame* frame) = 0;
   virtual void FinishDrawingFrame(DrawingFrame* frame) = 0;
   virtual void FinishDrawingQuadList();
diff --git a/cc/output/dynamic_geometry_binding.cc b/cc/output/dynamic_geometry_binding.cc
new file mode 100644
index 0000000..9e25892c
--- /dev/null
+++ b/cc/output/dynamic_geometry_binding.cc
@@ -0,0 +1,67 @@
+// Copyright 2015 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "cc/output/dynamic_geometry_binding.h"
+
+#include "cc/output/gl_renderer.h"  // For the GLC() macro.
+#include "gpu/command_buffer/client/gles2_interface.h"
+#include "ui/gfx/geometry/rect_f.h"
+
+namespace cc {
+
+DynamicGeometryBinding::DynamicGeometryBinding(gpu::gles2::GLES2Interface* gl)
+    : gl_(gl), quad_vertices_vbo_(0), quad_elements_vbo_(0) {
+  GeometryBindingQuad quads[1];
+  GeometryBindingQuadIndex quad_indices[1];
+
+  static_assert(sizeof(GeometryBindingQuad) == 24 * sizeof(float),
+                "struct Quad should be densely packed");
+  static_assert(sizeof(GeometryBindingQuadIndex) == 6 * sizeof(uint16_t),
+                "struct QuadIndex should be densely packed");
+
+  GLC(gl_, gl_->GenBuffers(1, &quad_vertices_vbo_));
+  GLC(gl_, gl_->GenBuffers(1, &quad_elements_vbo_));
+
+  GLC(gl_, gl_->BindBuffer(GL_ARRAY_BUFFER, quad_vertices_vbo_));
+  GLC(gl_, gl_->BufferData(GL_ARRAY_BUFFER, sizeof(GeometryBindingQuad) * 1,
+                           quads, GL_DYNAMIC_DRAW));
+
+  GLC(gl_, gl_->BindBuffer(GL_ELEMENT_ARRAY_BUFFER, quad_elements_vbo_));
+  GLC(gl_, gl_->BufferData(GL_ELEMENT_ARRAY_BUFFER,
+                           sizeof(GeometryBindingQuadIndex) * 1, &quad_indices,
+                           GL_DYNAMIC_DRAW));
+}
+
+void DynamicGeometryBinding::InitializeCustomQuad(const gfx::QuadF& quad) {
+  float uv[] = {0.0f, 0.0f, 1.0f, 0.0f, 1.0f, 1.0f, 0.0f, 1.0f};
+  InitializeCustomQuadWithUVs(quad, uv);
+}
+
+void DynamicGeometryBinding::InitializeCustomQuadWithUVs(const gfx::QuadF& quad,
+                                                         const float uv[8]) {
+  GeometryBindingVertex v0 = {
+      {quad.p1().x(), quad.p1().y(), 0.0f}, {uv[0], uv[1]}, 0.0f};
+  GeometryBindingVertex v1 = {
+      {quad.p2().x(), quad.p2().y(), 0.0f}, {uv[2], uv[3]}, 1.0f};
+  GeometryBindingVertex v2 = {
+      {quad.p3().x(), quad.p3().y(), 0.0f}, {uv[4], uv[5]}, 2.0f};
+  GeometryBindingVertex v3 = {
+      {quad.p4().x(), quad.p4().y(), 0.0f}, {uv[6], uv[7]}, 3.0f};
+
+  GeometryBindingQuad local_quad = {v0, v1, v2, v3};
+  GeometryBindingQuadIndex quad_index(
+      static_cast<uint16>(0), static_cast<uint16>(1), static_cast<uint16>(2),
+      static_cast<uint16>(3), static_cast<uint16>(0), static_cast<uint16>(2));
+
+  GLC(gl_, gl_->BufferSubData(GL_ARRAY_BUFFER, 0, sizeof(GeometryBindingQuad),
+                              &local_quad));
+  GLC(gl_, gl_->BufferSubData(GL_ELEMENT_ARRAY_BUFFER, 0,
+                              sizeof(GeometryBindingQuadIndex), &quad_index));
+}
+
+void DynamicGeometryBinding::PrepareForDraw() {
+  SetupGLContext(gl_, quad_elements_vbo_, quad_vertices_vbo_);
+}
+
+}  // namespace cc
diff --git a/cc/output/dynamic_geometry_binding.h b/cc/output/dynamic_geometry_binding.h
new file mode 100644
index 0000000..4e4ea3d
--- /dev/null
+++ b/cc/output/dynamic_geometry_binding.h
@@ -0,0 +1,30 @@
+// Copyright 2015 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef CC_OUTPUT_DYNAMIC_GEOMETRY_BINDING_H_
+#define CC_OUTPUT_DYNAMIC_GEOMETRY_BINDING_H_
+
+#include "cc/output/geometry_binding.h"
+
+namespace cc {
+
+class DynamicGeometryBinding {
+ public:
+  explicit DynamicGeometryBinding(gpu::gles2::GLES2Interface* gl);
+  void PrepareForDraw();
+  void InitializeCustomQuad(const gfx::QuadF& quad);
+  void InitializeCustomQuadWithUVs(const gfx::QuadF& quad, const float uv[8]);
+
+ private:
+  gpu::gles2::GLES2Interface* gl_;
+
+  GLuint quad_vertices_vbo_;
+  GLuint quad_elements_vbo_;
+
+  DISALLOW_COPY_AND_ASSIGN(DynamicGeometryBinding);
+};
+
+}  // namespace cc
+
+#endif  // CC_OUTPUT_DYNAMIC_GEOMETRY_BINDING_H_
diff --git a/cc/output/geometry_binding.cc b/cc/output/geometry_binding.cc
index eda2a5c..88a37deee 100644
--- a/cc/output/geometry_binding.cc
+++ b/cc/output/geometry_binding.cc
@@ -10,70 +10,12 @@
 
 namespace cc {
 
-GeometryBinding::GeometryBinding(gpu::gles2::GLES2Interface* gl,
-                                 const gfx::RectF& quad_vertex_rect)
-    : gl_(gl), quad_vertices_vbo_(0), quad_elements_vbo_(0) {
-  struct Vertex {
-    float a_position[3];
-    float a_texCoord[2];
-    // Index of the vertex, divide by 4 to have the matrix for this quad.
-    float a_index;
-  };
-  struct Quad {
-    Vertex v0, v1, v2, v3;
-  };
-  struct QuadIndex {
-    uint16 data[6];
-  };
+void SetupGLContext(gpu::gles2::GLES2Interface* gl,
+                    GLuint quad_elements_vbo,
+                    GLuint quad_vertices_vbo) {
+  GLC(gl, gl->BindBuffer(GL_ELEMENT_ARRAY_BUFFER, quad_elements_vbo));
 
-  static_assert(sizeof(Quad) == 24 * sizeof(float),
-                "struct Quad should be densely packed");
-  static_assert(sizeof(QuadIndex) == 6 * sizeof(uint16_t),
-                "struct QuadIndex should be densely packed");
-
-  Quad quad_list[8];
-  QuadIndex quad_index_list[8];
-  for (int i = 0; i < 8; i++) {
-    Vertex v0 = {{quad_vertex_rect.x(), quad_vertex_rect.bottom(), 0.0f, },
-                 {0.0f, 1.0f, }, i * 4.0f + 0.0f};
-    Vertex v1 = {{quad_vertex_rect.x(), quad_vertex_rect.y(), 0.0f, },
-                 {0.0f, 0.0f, }, i * 4.0f + 1.0f};
-    Vertex v2 = {{quad_vertex_rect.right(), quad_vertex_rect.y(), 0.0f, },
-                 {1.0f, .0f, }, i * 4.0f + 2.0f};
-    Vertex v3 = {{quad_vertex_rect.right(), quad_vertex_rect.bottom(), 0.0f, },
-                 {1.0f, 1.0f, }, i * 4.0f + 3.0f};
-    Quad x = {v0, v1, v2, v3};
-    quad_list[i] = x;
-    QuadIndex y = {
-        {static_cast<uint16>(0 + 4 * i), static_cast<uint16>(1 + 4 * i),
-         static_cast<uint16>(2 + 4 * i), static_cast<uint16>(3 + 4 * i),
-         static_cast<uint16>(0 + 4 * i), static_cast<uint16>(2 + 4 * i)}};
-    quad_index_list[i] = y;
-  }
-
-  gl_->GenBuffers(1, &quad_vertices_vbo_);
-  gl_->GenBuffers(1, &quad_elements_vbo_);
-  GLC(gl_, gl_->BindBuffer(GL_ARRAY_BUFFER, quad_vertices_vbo_));
-  GLC(gl_,
-      gl_->BufferData(
-          GL_ARRAY_BUFFER, sizeof(quad_list), quad_list, GL_STATIC_DRAW));
-  GLC(gl_, gl_->BindBuffer(GL_ELEMENT_ARRAY_BUFFER, quad_elements_vbo_));
-  GLC(gl_,
-      gl_->BufferData(GL_ELEMENT_ARRAY_BUFFER,
-                      sizeof(quad_index_list),
-                      quad_index_list,
-                      GL_STATIC_DRAW));
-}
-
-GeometryBinding::~GeometryBinding() {
-  gl_->DeleteBuffers(1, &quad_vertices_vbo_);
-  gl_->DeleteBuffers(1, &quad_elements_vbo_);
-}
-
-void GeometryBinding::PrepareForDraw() {
-  GLC(gl_, gl_->BindBuffer(GL_ELEMENT_ARRAY_BUFFER, quad_elements_vbo_));
-
-  GLC(gl_, gl_->BindBuffer(GL_ARRAY_BUFFER, quad_vertices_vbo_));
+  GLC(gl, gl->BindBuffer(GL_ARRAY_BUFFER, quad_vertices_vbo));
   // OpenGL defines the last parameter to VertexAttribPointer as type
   // "const GLvoid*" even though it is actually an offset into the buffer
   // object's data store and not a pointer to the client's address space.
@@ -83,15 +25,56 @@
       reinterpret_cast<const void*>(5 * sizeof(float)),
   };
 
-  GLC(gl_, gl_->VertexAttribPointer(PositionAttribLocation(), 3, GL_FLOAT,
-                                    false, 6 * sizeof(float), offsets[0]));
-  GLC(gl_, gl_->VertexAttribPointer(TexCoordAttribLocation(), 2, GL_FLOAT,
-                                    false, 6 * sizeof(float), offsets[1]));
-  GLC(gl_, gl_->VertexAttribPointer(TriangleIndexAttribLocation(), 1, GL_FLOAT,
-                                    false, 6 * sizeof(float), offsets[2]));
-  GLC(gl_, gl_->EnableVertexAttribArray(PositionAttribLocation()));
-  GLC(gl_, gl_->EnableVertexAttribArray(TexCoordAttribLocation()));
-  GLC(gl_, gl_->EnableVertexAttribArray(TriangleIndexAttribLocation()));
+  GLC(gl,
+      gl->VertexAttribPointer(GeometryBinding::PositionAttribLocation(), 3,
+                              GL_FLOAT, false, 6 * sizeof(float), offsets[0]));
+  GLC(gl,
+      gl->VertexAttribPointer(GeometryBinding::TexCoordAttribLocation(), 2,
+                              GL_FLOAT, false, 6 * sizeof(float), offsets[1]));
+  GLC(gl,
+      gl->VertexAttribPointer(GeometryBinding::TriangleIndexAttribLocation(), 1,
+                              GL_FLOAT, false, 6 * sizeof(float), offsets[2]));
+  GLC(gl,
+      gl->EnableVertexAttribArray(GeometryBinding::PositionAttribLocation()));
+  GLC(gl,
+      gl->EnableVertexAttribArray(GeometryBinding::TexCoordAttribLocation()));
+  GLC(gl, gl->EnableVertexAttribArray(
+              GeometryBinding::TriangleIndexAttribLocation()));
+}
+
+GeometryBindingQuad::GeometryBindingQuad() {
+  v0 = {{0, 0, 0}, {0, 0}, 0};
+  v1 = {{0, 0, 0}, {0, 0}, 0};
+  v2 = {{0, 0, 0}, {0, 0}, 0};
+  v3 = {{0, 0, 0}, {0, 0}, 0};
+}
+
+GeometryBindingQuad::GeometryBindingQuad(const GeometryBindingVertex& vert0,
+                                         const GeometryBindingVertex& vert1,
+                                         const GeometryBindingVertex& vert2,
+                                         const GeometryBindingVertex& vert3) {
+  v0 = vert0;
+  v1 = vert1;
+  v2 = vert2;
+  v3 = vert3;
+}
+
+GeometryBindingQuadIndex::GeometryBindingQuadIndex() {
+  memset(data, 0x0, sizeof(data));
+}
+
+GeometryBindingQuadIndex::GeometryBindingQuadIndex(uint16 index0,
+                                                   uint16 index1,
+                                                   uint16 index2,
+                                                   uint16 index3,
+                                                   uint16 index4,
+                                                   uint16 index5) {
+  data[0] = index0;
+  data[1] = index1;
+  data[2] = index2;
+  data[3] = index3;
+  data[4] = index4;
+  data[5] = index5;
 }
 
 }  // namespace cc
diff --git a/cc/output/geometry_binding.h b/cc/output/geometry_binding.h
index cfa21ef..ae68687 100644
--- a/cc/output/geometry_binding.h
+++ b/cc/output/geometry_binding.h
@@ -6,43 +6,65 @@
 #define CC_OUTPUT_GEOMETRY_BINDING_H_
 
 #include "base/basictypes.h"
+#include "cc/output/gl_renderer.h"  // For the GLC() macro.
+#include "gpu/command_buffer/client/gles2_interface.h"
 #include "third_party/khronos/GLES2/gl2.h"
+#include "third_party/khronos/GLES2/gl2ext.h"
+#include "ui/gfx/geometry/rect_f.h"
 
 namespace gfx {
-class RectF;
-}
-namespace gpu {
-namespace gles2 {
-class GLES2Interface;
-}
+class QuadF;
+class Quad;
+class QuadIndex;
+class PointF;
 }
 
 namespace cc {
 
-class GeometryBinding {
- public:
-  GeometryBinding(gpu::gles2::GLES2Interface* gl,
-                  const gfx::RectF& quad_vertex_rect);
-  ~GeometryBinding();
+struct GeometryBindingVertex {
+  float a_position[3];
+  float a_texCoord[2];
+  // Index of the vertex, divide by 4 to have the matrix for this quad.
+  float a_index;
+};
 
-  void PrepareForDraw();
+struct GeometryBindingQuad {
+  GeometryBindingQuad();
+  GeometryBindingQuad(const GeometryBindingVertex& vert0,
+                      const GeometryBindingVertex& vert1,
+                      const GeometryBindingVertex& vert2,
+                      const GeometryBindingVertex& vert3);
+  GeometryBindingVertex v0, v1, v2, v3;
+};
 
+struct GeometryBindingQuadIndex {
+  GeometryBindingQuadIndex();
+  GeometryBindingQuadIndex(uint16 index0,
+                           uint16 index1,
+                           uint16 index2,
+                           uint16 index3,
+                           uint16 index4,
+                           uint16 index5);
+
+  uint16 data[6];
+};
+
+class DrawQuad;
+class DrawPolygon;
+
+struct GeometryBinding {
   // All layer shaders share the same attribute locations for the vertex
   // positions and texture coordinates. This allows switching shaders without
   // rebinding attribute arrays.
   static int PositionAttribLocation() { return 0; }
   static int TexCoordAttribLocation() { return 1; }
   static int TriangleIndexAttribLocation() { return 2; }
-
- private:
-  gpu::gles2::GLES2Interface* gl_;
-
-  GLuint quad_vertices_vbo_;
-  GLuint quad_elements_vbo_;
-
-  DISALLOW_COPY_AND_ASSIGN(GeometryBinding);
 };
 
+void SetupGLContext(gpu::gles2::GLES2Interface* gl,
+                    GLuint quad_elements_vbo,
+                    GLuint quad_vertices_vbo);
+
 }  // namespace cc
 
 #endif  // CC_OUTPUT_GEOMETRY_BINDING_H_
diff --git a/cc/output/gl_renderer.cc b/cc/output/gl_renderer.cc
index 2016447..613ea4b 100644
--- a/cc/output/gl_renderer.cc
+++ b/cc/output/gl_renderer.cc
@@ -11,6 +11,11 @@
 #include <vector>
 
 #include "base/logging.h"
+#include "base/memory/scoped_ptr.h"
+#include "base/strings/string_split.h"
+#include "base/strings/string_util.h"
+#include "base/strings/stringprintf.h"
+#include "build/build_config.h"
 #include "base/trace_event/trace_event.h"
 #include "cc/base/math_util.h"
 #include "cc/layers/video_layer_impl.h"
@@ -18,10 +23,12 @@
 #include "cc/output/compositor_frame_metadata.h"
 #include "cc/output/context_provider.h"
 #include "cc/output/copy_output_request.h"
-#include "cc/output/geometry_binding.h"
+#include "cc/output/dynamic_geometry_binding.h"
 #include "cc/output/gl_frame_data.h"
 #include "cc/output/output_surface.h"
 #include "cc/output/render_surface_filters.h"
+#include "cc/output/static_geometry_binding.h"
+#include "cc/quads/draw_polygon.h"
 #include "cc/quads/picture_draw_quad.h"
 #include "cc/quads/render_pass.h"
 #include "cc/quads/stream_video_draw_quad.h"
@@ -324,7 +331,8 @@
       highp_threshold_min_(highp_threshold_min),
       highp_threshold_cache_(0),
       use_sync_query_(false),
-      on_demand_tile_raster_resource_id_(0) {
+      on_demand_tile_raster_resource_id_(0),
+      bound_geometry_(NO_BINDING) {
   DCHECK(gl_);
   DCHECK(context_support_);
 
@@ -494,10 +502,12 @@
   GLC(gl_, gl_->Flush());
 }
 
-void GLRenderer::DoDrawQuad(DrawingFrame* frame, const DrawQuad* quad) {
+void GLRenderer::DoDrawQuad(DrawingFrame* frame,
+                            const DrawQuad* quad,
+                            const gfx::QuadF* clip_region) {
   DCHECK(quad->rect.Contains(quad->visible_rect));
   if (quad->material != DrawQuad::TEXTURE_CONTENT) {
-    FlushTextureQuadCache();
+    FlushTextureQuadCache(SHARED_BINDING);
   }
 
   switch (quad->material) {
@@ -505,26 +515,31 @@
       NOTREACHED();
       break;
     case DrawQuad::CHECKERBOARD:
-      DrawCheckerboardQuad(frame, CheckerboardDrawQuad::MaterialCast(quad));
+      DrawCheckerboardQuad(frame, CheckerboardDrawQuad::MaterialCast(quad),
+                           clip_region);
       break;
     case DrawQuad::DEBUG_BORDER:
       DrawDebugBorderQuad(frame, DebugBorderDrawQuad::MaterialCast(quad));
       break;
     case DrawQuad::IO_SURFACE_CONTENT:
-      DrawIOSurfaceQuad(frame, IOSurfaceDrawQuad::MaterialCast(quad));
+      DrawIOSurfaceQuad(frame, IOSurfaceDrawQuad::MaterialCast(quad),
+                        clip_region);
       break;
     case DrawQuad::PICTURE_CONTENT:
       // PictureDrawQuad should only be used for resourceless software draws.
       NOTREACHED();
       break;
     case DrawQuad::RENDER_PASS:
-      DrawRenderPassQuad(frame, RenderPassDrawQuad::MaterialCast(quad));
+      DrawRenderPassQuad(frame, RenderPassDrawQuad::MaterialCast(quad),
+                         clip_region);
       break;
     case DrawQuad::SOLID_COLOR:
-      DrawSolidColorQuad(frame, SolidColorDrawQuad::MaterialCast(quad));
+      DrawSolidColorQuad(frame, SolidColorDrawQuad::MaterialCast(quad),
+                         clip_region);
       break;
     case DrawQuad::STREAM_VIDEO_CONTENT:
-      DrawStreamVideoQuad(frame, StreamVideoDrawQuad::MaterialCast(quad));
+      DrawStreamVideoQuad(frame, StreamVideoDrawQuad::MaterialCast(quad),
+                          clip_region);
       break;
     case DrawQuad::SURFACE_CONTENT:
       // Surface content should be fully resolved to other quad types before
@@ -532,19 +547,28 @@
       NOTREACHED();
       break;
     case DrawQuad::TEXTURE_CONTENT:
-      EnqueueTextureQuad(frame, TextureDrawQuad::MaterialCast(quad));
+      EnqueueTextureQuad(frame, TextureDrawQuad::MaterialCast(quad),
+                         clip_region);
       break;
     case DrawQuad::TILED_CONTENT:
-      DrawTileQuad(frame, TileDrawQuad::MaterialCast(quad));
+      DrawTileQuad(frame, TileDrawQuad::MaterialCast(quad), clip_region);
       break;
     case DrawQuad::YUV_VIDEO_CONTENT:
-      DrawYUVVideoQuad(frame, YUVVideoDrawQuad::MaterialCast(quad));
+      DrawYUVVideoQuad(frame, YUVVideoDrawQuad::MaterialCast(quad),
+                       clip_region);
       break;
   }
 }
 
 void GLRenderer::DrawCheckerboardQuad(const DrawingFrame* frame,
-                                      const CheckerboardDrawQuad* quad) {
+                                      const CheckerboardDrawQuad* quad,
+                                      const gfx::QuadF* clip_region) {
+  // TODO(enne) For now since checkerboards shouldn't be part of a 3D
+  // context, clipping regions aren't supported so we skip drawing them
+  // if this becomes the case.
+  if (clip_region) {
+    return;
+  }
   SetBlendEnabled(quad->ShouldDrawWithBlending());
 
   const TileCheckerboardProgram* program = GetTileCheckerboardProgram();
@@ -588,6 +612,9 @@
                    program->vertex_shader().matrix_location());
 }
 
+// This function does not handle 3D sorting right now, since the debug border
+// quads are just drawn as their original quads and not in split pieces. This
+// results in some debug border quads drawing over foreground quads.
 void GLRenderer::DrawDebugBorderQuad(const DrawingFrame* frame,
                                      const DebugBorderDrawQuad* quad) {
   SetBlendEnabled(quad->ShouldDrawWithBlending());
@@ -819,13 +846,56 @@
   return true;
 }
 
+// This takes a gfx::Rect and a clip region quad in the same space,
+// and returns a quad with the same proportions in the space -0.5->0.5.
+bool GetScaledRegion(const gfx::Rect& rect,
+                     const gfx::QuadF* clip,
+                     gfx::QuadF* scaled_region) {
+  if (!clip)
+    return false;
+
+  gfx::PointF p1(((clip->p1().x() - rect.x()) / rect.width()) - 0.5f,
+                 ((clip->p1().y() - rect.y()) / rect.height()) - 0.5f);
+  gfx::PointF p2(((clip->p2().x() - rect.x()) / rect.width()) - 0.5f,
+                 ((clip->p2().y() - rect.y()) / rect.height()) - 0.5f);
+  gfx::PointF p3(((clip->p3().x() - rect.x()) / rect.width()) - 0.5f,
+                 ((clip->p3().y() - rect.y()) / rect.height()) - 0.5f);
+  gfx::PointF p4(((clip->p4().x() - rect.x()) / rect.width()) - 0.5f,
+                 ((clip->p4().y() - rect.y()) / rect.height()) - 0.5f);
+  *scaled_region = gfx::QuadF(p1, p2, p3, p4);
+  return true;
+}
+
+// This takes a gfx::Rect and a clip region quad in the same space,
+// and returns the proportional uv's in the space 0->1.
+bool GetScaledUVs(const gfx::Rect& rect, const gfx::QuadF* clip, float uvs[8]) {
+  if (!clip)
+    return false;
+
+  uvs[0] = ((clip->p1().x() - rect.x()) / rect.width());
+  uvs[1] = ((clip->p1().y() - rect.y()) / rect.height());
+  uvs[2] = ((clip->p2().x() - rect.x()) / rect.width());
+  uvs[3] = ((clip->p2().y() - rect.y()) / rect.height());
+  uvs[4] = ((clip->p3().x() - rect.x()) / rect.width());
+  uvs[5] = ((clip->p3().y() - rect.y()) / rect.height());
+  uvs[6] = ((clip->p4().x() - rect.x()) / rect.width());
+  uvs[7] = ((clip->p4().y() - rect.y()) / rect.height());
+  return true;
+}
+
 gfx::Rect GLRenderer::GetBackdropBoundingBoxForRenderPassQuad(
     DrawingFrame* frame,
     const RenderPassDrawQuad* quad,
     const gfx::Transform& contents_device_transform,
+    const gfx::QuadF* clip_region,
     bool use_aa) {
+  gfx::QuadF scaled_region;
+  if (!GetScaledRegion(quad->rect, clip_region, &scaled_region)) {
+    scaled_region = SharedGeometryQuad().BoundingBox();
+  }
+
   gfx::Rect backdrop_rect = gfx::ToEnclosingRect(MathUtil::MapClippedRect(
-      contents_device_transform, SharedGeometryQuad().BoundingBox()));
+      contents_device_transform, scaled_region.BoundingBox()));
 
   if (ShouldApplyBackgroundFilters(frame, quad)) {
     int top, right, bottom, left;
@@ -874,7 +944,8 @@
 }
 
 void GLRenderer::DrawRenderPassQuad(DrawingFrame* frame,
-                                    const RenderPassDrawQuad* quad) {
+                                    const RenderPassDrawQuad* quad,
+                                    const gfx::QuadF* clip_region) {
   ScopedResource* contents_texture =
       render_pass_textures_.get(quad->render_pass_id);
   if (!contents_texture || !contents_texture->id())
@@ -896,10 +967,8 @@
                 ShouldAntialiasQuad(contents_device_transform, quad,
                                     settings_->force_antialiasing);
 
-  if (use_aa)
-    SetupQuadForAntialiasing(contents_device_transform, quad,
-                             &surface_quad, edge);
-
+  SetupQuadForClippingAndAntialiasing(contents_device_transform, quad, use_aa,
+                                      clip_region, &surface_quad, edge);
   SkXfermode::Mode blend_mode = quad->shared_quad_state->blend_mode;
   bool use_shaders_for_blending =
       !CanApplyBlendModeUsingBlendFunc(blend_mode) ||
@@ -913,7 +982,7 @@
     // Compute a bounding box around the pixels that will be visible through
     // the quad.
     background_rect = GetBackdropBoundingBoxForRenderPassQuad(
-        frame, quad, contents_device_transform, use_aa);
+        frame, quad, contents_device_transform, clip_region, use_aa);
 
     if (!background_rect.IsEmpty()) {
       // The pixels from the filtered background should completely replace the
@@ -1238,15 +1307,78 @@
   uniforms->color_location = program->fragment_shader().color_location();
 }
 
+namespace {
+// These functions determine if a quad, clipped by a clip_region contains
+// the entire {top|bottom|left|right} edge.
+bool is_top(const gfx::QuadF* clip_region, const DrawQuad* quad) {
+  if (!quad->IsTopEdge())
+    return false;
+  if (!clip_region)
+    return true;
+
+  return std::abs(clip_region->p1().y()) < kAntiAliasingEpsilon &&
+         std::abs(clip_region->p2().y()) < kAntiAliasingEpsilon;
+}
+
+bool is_bottom(const gfx::QuadF* clip_region, const DrawQuad* quad) {
+  if (!quad->IsBottomEdge())
+    return false;
+  if (!clip_region)
+    return true;
+
+  return std::abs(clip_region->p3().y() -
+                  quad->shared_quad_state->content_bounds.height()) <
+             kAntiAliasingEpsilon &&
+         std::abs(clip_region->p4().y() -
+                  quad->shared_quad_state->content_bounds.height()) <
+             kAntiAliasingEpsilon;
+}
+
+bool is_left(const gfx::QuadF* clip_region, const DrawQuad* quad) {
+  if (!quad->IsLeftEdge())
+    return false;
+  if (!clip_region)
+    return true;
+
+  return std::abs(clip_region->p1().x()) < kAntiAliasingEpsilon &&
+         std::abs(clip_region->p4().x()) < kAntiAliasingEpsilon;
+}
+
+bool is_right(const gfx::QuadF* clip_region, const DrawQuad* quad) {
+  if (!quad->IsRightEdge())
+    return false;
+  if (!clip_region)
+    return true;
+
+  return std::abs(clip_region->p2().x() -
+                  quad->shared_quad_state->content_bounds.width()) <
+             kAntiAliasingEpsilon &&
+         std::abs(clip_region->p3().x() -
+                  quad->shared_quad_state->content_bounds.width()) <
+             kAntiAliasingEpsilon;
+}
+}  // anonymous namespace
+
 static gfx::QuadF GetDeviceQuadWithAntialiasingOnExteriorEdges(
     const LayerQuad& device_layer_edges,
     const gfx::Transform& device_transform,
+    const gfx::QuadF* clip_region,
     const DrawQuad* quad) {
-  gfx::Rect tile_rect = quad->visible_rect;
-  gfx::PointF bottom_right = tile_rect.bottom_right();
-  gfx::PointF bottom_left = tile_rect.bottom_left();
-  gfx::PointF top_left = tile_rect.origin();
-  gfx::PointF top_right = tile_rect.top_right();
+  gfx::RectF tile_rect = quad->visible_rect;
+  gfx::QuadF tile_quad(tile_rect);
+
+  if (clip_region) {
+    if (quad->material != DrawQuad::RENDER_PASS) {
+      tile_quad = *clip_region;
+    } else {
+      GetScaledRegion(quad->rect, clip_region, &tile_quad);
+    }
+  }
+
+  gfx::PointF bottom_right = tile_quad.p3();
+  gfx::PointF bottom_left = tile_quad.p4();
+  gfx::PointF top_left = tile_quad.p1();
+  gfx::PointF top_right = tile_quad.p2();
   bool clipped = false;
 
   // Map points to device space. We ignore |clipped|, since the result of
@@ -1263,16 +1395,26 @@
   LayerQuad::Edge right_edge(top_right, bottom_right);
 
   // Only apply anti-aliasing to edges not clipped by culling or scissoring.
-  if (quad->IsTopEdge() && tile_rect.y() == quad->rect.y())
+  // If an edge is degenerate we do not want to replace it with a "proper" edge
+  // as that will cause the quad to possibly expand is strange ways.
+  if (!top_edge.degenerate() && is_top(clip_region, quad) &&
+      tile_rect.y() == quad->rect.y()) {
     top_edge = device_layer_edges.top();
-  if (quad->IsLeftEdge() && tile_rect.x() == quad->rect.x())
+  }
+  if (!left_edge.degenerate() && is_left(clip_region, quad) &&
+      tile_rect.x() == quad->rect.x()) {
     left_edge = device_layer_edges.left();
-  if (quad->IsRightEdge() && tile_rect.right() == quad->rect.right())
+  }
+  if (!right_edge.degenerate() && is_right(clip_region, quad) &&
+      tile_rect.right() == quad->rect.right()) {
     right_edge = device_layer_edges.right();
-  if (quad->IsBottomEdge() && tile_rect.bottom() == quad->rect.bottom())
+  }
+  if (!bottom_edge.degenerate() && is_bottom(clip_region, quad) &&
+      tile_rect.bottom() == quad->rect.bottom()) {
     bottom_edge = device_layer_edges.bottom();
+  }
 
-  float sign = gfx::QuadF(tile_rect).IsCounterClockwise() ? -1 : 1;
+  float sign = tile_quad.IsCounterClockwise() ? -1 : 1;
   bottom_edge.scale(sign);
   left_edge.scale(sign);
   top_edge.scale(sign);
@@ -1282,6 +1424,32 @@
   return LayerQuad(left_edge, top_edge, right_edge, bottom_edge).ToQuadF();
 }
 
+float GetTotalQuadError(const gfx::QuadF* clipped_quad,
+                        const gfx::QuadF* ideal_rect) {
+  return (clipped_quad->p1() - ideal_rect->p1()).LengthSquared() +
+         (clipped_quad->p2() - ideal_rect->p2()).LengthSquared() +
+         (clipped_quad->p3() - ideal_rect->p3()).LengthSquared() +
+         (clipped_quad->p4() - ideal_rect->p4()).LengthSquared();
+}
+
+// Attempt to rotate the clipped quad until it lines up the most
+// correctly. This is necessary because we check the edges of this
+// quad against the expected left/right/top/bottom for anti-aliasing.
+void AlignQuadToBoundingBox(gfx::QuadF* clipped_quad) {
+  gfx::QuadF bounding_quad = gfx::QuadF(clipped_quad->BoundingBox());
+  gfx::QuadF best_rotation = *clipped_quad;
+  float least_error_amount = GetTotalQuadError(clipped_quad, &bounding_quad);
+  for (size_t i = 1; i < 4; ++i) {
+    clipped_quad->Realign(1);
+    float new_error = GetTotalQuadError(clipped_quad, &bounding_quad);
+    if (new_error < least_error_amount) {
+      least_error_amount = new_error;
+      best_rotation = *clipped_quad;
+    }
+  }
+  *clipped_quad = best_rotation;
+}
+
 // static
 bool GLRenderer::ShouldAntialiasQuad(const gfx::Transform& device_transform,
                                      const DrawQuad* quad,
@@ -1313,18 +1481,39 @@
 }
 
 // static
-void GLRenderer::SetupQuadForAntialiasing(
+void GLRenderer::SetupQuadForClippingAndAntialiasing(
     const gfx::Transform& device_transform,
     const DrawQuad* quad,
+    bool use_aa,
+    const gfx::QuadF* clip_region,
     gfx::QuadF* local_quad,
     float edge[24]) {
   bool is_render_pass_quad = (quad->material == DrawQuad::RENDER_PASS);
-  gfx::RectF content_rect =
-      is_render_pass_quad ? QuadVertexRect() : quad->visibleContentRect();
+  gfx::QuadF rotated_clip;
+  const gfx::QuadF* local_clip_region = clip_region;
+  if (local_clip_region) {
+    rotated_clip = *clip_region;
+    AlignQuadToBoundingBox(&rotated_clip);
+    local_clip_region = &rotated_clip;
+  }
 
+  gfx::QuadF content_rect = is_render_pass_quad
+                                ? gfx::QuadF(QuadVertexRect())
+                                : gfx::QuadF(quad->visibleContentRect());
+  if (!use_aa) {
+    if (local_clip_region) {
+      if (!is_render_pass_quad) {
+        content_rect = *local_clip_region;
+      } else {
+        GetScaledRegion(quad->rect, local_clip_region, &content_rect);
+      }
+      *local_quad = content_rect;
+    }
+    return;
+  }
   bool clipped = false;
   gfx::QuadF device_layer_quad =
-      MathUtil::MapQuad(device_transform, gfx::QuadF(content_rect), &clipped);
+      MathUtil::MapQuad(device_transform, content_rect, &clipped);
 
   LayerQuad device_layer_bounds(gfx::QuadF(device_layer_quad.BoundingBox()));
   device_layer_bounds.InflateAntiAliasingDistance();
@@ -1335,16 +1524,25 @@
   device_layer_edges.ToFloatArray(edge);
   device_layer_bounds.ToFloatArray(&edge[12]);
 
+  // If we have a clip region then we are split, and therefore
+  // by necessity, at least one of our edges is not an external
+  // one.
+  bool is_full_rect = quad->visible_rect == quad->rect;
+
+  bool region_contains_all_outside_edges =
+      is_full_rect &&
+      (is_top(local_clip_region, quad) && is_left(local_clip_region, quad) &&
+       is_bottom(local_clip_region, quad) && is_right(local_clip_region, quad));
+
   bool use_aa_on_all_four_edges =
-      is_render_pass_quad ||
-      (quad->IsTopEdge() && quad->IsLeftEdge() && quad->IsBottomEdge() &&
-       quad->IsRightEdge() && quad->visible_rect == quad->rect);
+      !local_clip_region &&
+      (is_render_pass_quad || region_contains_all_outside_edges);
 
   gfx::QuadF device_quad =
       use_aa_on_all_four_edges
           ? device_layer_edges.ToQuadF()
           : GetDeviceQuadWithAntialiasingOnExteriorEdges(
-                device_layer_edges, device_transform, quad);
+                device_layer_edges, device_transform, local_clip_region, quad);
 
   // Map device space quad to local space. device_transform has no 3d
   // component since it was flattened, so we don't need to project.  We should
@@ -1360,7 +1558,8 @@
 }
 
 void GLRenderer::DrawSolidColorQuad(const DrawingFrame* frame,
-                                    const SolidColorDrawQuad* quad) {
+                                    const SolidColorDrawQuad* quad,
+                                    const gfx::QuadF* clip_region) {
   gfx::Rect tile_rect = quad->visible_rect;
 
   SkColor color = quad->color;
@@ -1384,10 +1583,11 @@
   bool use_aa = settings_->allow_antialiasing &&
                 !quad->force_anti_aliasing_off &&
                 ShouldAntialiasQuad(device_transform, quad, force_aa);
+  SetupQuadForClippingAndAntialiasing(device_transform, quad, use_aa,
+                                      clip_region, &local_quad, edge);
 
   SolidColorProgramUniforms uniforms;
   if (use_aa) {
-    SetupQuadForAntialiasing(device_transform, quad, &local_quad, edge);
     SolidColorUniformLocation(GetSolidColorProgramAA(), &uniforms);
   } else {
     SolidColorUniformLocation(GetSolidColorProgram(), &uniforms);
@@ -1458,13 +1658,15 @@
 }
 
 void GLRenderer::DrawTileQuad(const DrawingFrame* frame,
-                              const TileDrawQuad* quad) {
-  DrawContentQuad(frame, quad, quad->resource_id);
+                              const TileDrawQuad* quad,
+                              const gfx::QuadF* clip_region) {
+  DrawContentQuad(frame, quad, quad->resource_id, clip_region);
 }
 
 void GLRenderer::DrawContentQuad(const DrawingFrame* frame,
                                  const ContentDrawQuadBase* quad,
-                                 ResourceProvider::ResourceId resource_id) {
+                                 ResourceProvider::ResourceId resource_id,
+                                 const gfx::QuadF* clip_region) {
   gfx::Transform device_transform =
       frame->window_matrix * frame->projection_matrix * quad->quadTransform();
   device_transform.FlattenTo2d();
@@ -1476,15 +1678,16 @@
   // similar to the way DrawContentQuadNoAA works and then consider
   // combining DrawContentQuadAA and DrawContentQuadNoAA into one method.
   if (use_aa)
-    DrawContentQuadAA(frame, quad, resource_id, device_transform);
+    DrawContentQuadAA(frame, quad, resource_id, device_transform, clip_region);
   else
-    DrawContentQuadNoAA(frame, quad, resource_id);
+    DrawContentQuadNoAA(frame, quad, resource_id, clip_region);
 }
 
 void GLRenderer::DrawContentQuadAA(const DrawingFrame* frame,
                                    const ContentDrawQuadBase* quad,
                                    ResourceProvider::ResourceId resource_id,
-                                   const gfx::Transform& device_transform) {
+                                   const gfx::Transform& device_transform,
+                                   const gfx::QuadF* clip_region) {
   if (!device_transform.IsInvertible())
     return;
 
@@ -1529,8 +1732,8 @@
 
   gfx::QuadF local_quad = gfx::QuadF(gfx::RectF(tile_rect));
   float edge[24];
-  SetupQuadForAntialiasing(device_transform, quad, &local_quad, edge);
-
+  SetupQuadForClippingAndAntialiasing(device_transform, quad, true, clip_region,
+                                      &local_quad, edge);
   ResourceProvider::ScopedSamplerGL quad_resource_lock(
       resource_provider_, resource_id,
       quad->nearest_neighbor ? GL_NEAREST : GL_LINEAR);
@@ -1608,7 +1811,8 @@
 
 void GLRenderer::DrawContentQuadNoAA(const DrawingFrame* frame,
                                      const ContentDrawQuadBase* quad,
-                                     ResourceProvider::ResourceId resource_id) {
+                                     ResourceProvider::ResourceId resource_id,
+                                     const gfx::QuadF* clip_region) {
   gfx::RectF tex_coord_rect = MathUtil::ScaleRectProportional(
       quad->tex_coord_rect, quad->rect, quad->visible_rect);
   float tex_to_geom_scale_x = quad->rect.width() / quad->tex_coord_rect.width();
@@ -1682,17 +1886,37 @@
   // does, then vertices will match the texture mapping in the vertex buffer.
   // The method SetShaderQuadF() changes the order of vertices and so it's
   // not used here.
-
-  gfx::RectF tile_rect = quad->visible_rect;
+  gfx::QuadF tile_rect(quad->visible_rect);
+  float width = quad->visible_rect.width();
+  float height = quad->visible_rect.height();
+  gfx::PointF top_left = quad->visible_rect.origin();
+  if (clip_region) {
+    tile_rect = *clip_region;
+    float gl_uv[8] = {
+        (tile_rect.p4().x() - top_left.x()) / width,
+        (tile_rect.p4().y() - top_left.y()) / height,
+        (tile_rect.p1().x() - top_left.x()) / width,
+        (tile_rect.p1().y() - top_left.y()) / height,
+        (tile_rect.p2().x() - top_left.x()) / width,
+        (tile_rect.p2().y() - top_left.y()) / height,
+        (tile_rect.p3().x() - top_left.x()) / width,
+        (tile_rect.p3().y() - top_left.y()) / height,
+    };
+    PrepareGeometry(CLIPPED_BINDING);
+    clipped_geometry_->InitializeCustomQuadWithUVs(
+        gfx::QuadF(quad->visible_rect), gl_uv);
+  } else {
+    PrepareGeometry(SHARED_BINDING);
+  }
   float gl_quad[8] = {
-    tile_rect.x(),
-    tile_rect.bottom(),
-    tile_rect.x(),
-    tile_rect.y(),
-    tile_rect.right(),
-    tile_rect.y(),
-    tile_rect.right(),
-    tile_rect.bottom(),
+      tile_rect.p4().x(),
+      tile_rect.p4().y(),
+      tile_rect.p1().x(),
+      tile_rect.p1().y(),
+      tile_rect.p2().x(),
+      tile_rect.p2().y(),
+      tile_rect.p3().x(),
+      tile_rect.p3().y(),
   };
   GLC(gl_, gl_->Uniform2fv(uniforms.quad_location, 4, gl_quad));
 
@@ -1705,7 +1929,8 @@
 }
 
 void GLRenderer::DrawYUVVideoQuad(const DrawingFrame* frame,
-                                  const YUVVideoDrawQuad* quad) {
+                                  const YUVVideoDrawQuad* quad,
+                                  const gfx::QuadF* clip_region) {
   SetBlendEnabled(quad->ShouldDrawWithBlending());
 
   TexCoordPrecision tex_coord_precision = TexCoordPrecisionRequired(
@@ -1845,15 +2070,31 @@
       break;
   }
 
+  // The transform and vertex data are used to figure out the extents that the
+  // un-antialiased quad should have and which vertex this is and the float
+  // quad passed in via uniform is the actual geometry that gets used to draw
+  // it. This is why this centered rect is used and not the original quad_rect.
+  gfx::RectF tile_rect = quad->visible_rect;
   GLC(gl_, gl_->UniformMatrix3fv(yuv_matrix_location, 1, 0, yuv_to_rgb));
   GLC(gl_, gl_->Uniform3fv(yuv_adj_location, 1, yuv_adjust));
 
   SetShaderOpacity(quad->opacity(), alpha_location);
-  DrawQuadGeometry(frame, quad->quadTransform(), quad->rect, matrix_location);
+  if (!clip_region) {
+    DrawQuadGeometry(frame, quad->quadTransform(), tile_rect, matrix_location);
+  } else {
+    float uvs[8] = {0};
+    GetScaledUVs(quad->visible_rect, clip_region, uvs);
+    gfx::QuadF region_quad = *clip_region;
+    region_quad.Scale(1.0f / tile_rect.width(), 1.0f / tile_rect.height());
+    region_quad -= gfx::Vector2dF(0.5f, 0.5f);
+    DrawQuadGeometryClippedByQuadF(frame, quad->quadTransform(), tile_rect,
+                                   region_quad, matrix_location, uvs);
+  }
 }
 
 void GLRenderer::DrawStreamVideoQuad(const DrawingFrame* frame,
-                                     const StreamVideoDrawQuad* quad) {
+                                     const StreamVideoDrawQuad* quad,
+                                     const gfx::QuadF* clip_region) {
   SetBlendEnabled(quad->ShouldDrawWithBlending());
 
   static float gl_matrix[16];
@@ -1884,10 +2125,19 @@
 
   SetShaderOpacity(quad->opacity(),
                    program->fragment_shader().alpha_location());
-  DrawQuadGeometry(frame,
-                   quad->quadTransform(),
-                   quad->rect,
-                   program->vertex_shader().matrix_location());
+  if (!clip_region) {
+    DrawQuadGeometry(frame, quad->quadTransform(), quad->rect,
+                     program->vertex_shader().matrix_location());
+  } else {
+    gfx::QuadF region_quad(*clip_region);
+    region_quad.Scale(1.0f / quad->rect.width(), 1.0f / quad->rect.height());
+    region_quad -= gfx::Vector2dF(0.5f, 0.5f);
+    float uvs[8] = {0};
+    GetScaledUVs(quad->visible_rect, clip_region, uvs);
+    DrawQuadGeometryClippedByQuadF(
+        frame, quad->quadTransform(), quad->rect, region_quad,
+        program->vertex_shader().matrix_location(), uvs);
+  }
 }
 
 struct TextureProgramBinding {
@@ -1903,6 +2153,7 @@
   int program_id;
   int sampler_location;
   int matrix_location;
+  int transform_location;
   int background_color_location;
 };
 
@@ -1918,11 +2169,13 @@
   int vertex_opacity_location;
 };
 
-void GLRenderer::FlushTextureQuadCache() {
+void GLRenderer::FlushTextureQuadCache(BoundGeometry flush_binding) {
   // Check to see if we have anything to draw.
   if (draw_cache_.program_id == -1)
     return;
 
+  PrepareGeometry(flush_binding);
+
   // Set the correct blending mode.
   SetBlendEnabled(draw_cache_.needs_blending);
 
@@ -1983,10 +2236,26 @@
   draw_cache_.uv_xform_data.resize(0);
   draw_cache_.vertex_opacity_data.resize(0);
   draw_cache_.matrix_data.resize(0);
+
+  // If we had a clipped binding, prepare the shared binding for the
+  // next inserts.
+  if (flush_binding == CLIPPED_BINDING) {
+    PrepareGeometry(SHARED_BINDING);
+  }
 }
 
 void GLRenderer::EnqueueTextureQuad(const DrawingFrame* frame,
-                                    const TextureDrawQuad* quad) {
+                                    const TextureDrawQuad* quad,
+                                    const gfx::QuadF* clip_region) {
+  // If we have a clip_region then we have to render the next quad
+  // with dynamic geometry, therefore we must flush all pending
+  // texture quads.
+  if (clip_region) {
+    // We send in false here because we want to flush what's currently in the
+    // queue using the shared_geometry and not clipped_geometry
+    FlushTextureQuadCache(SHARED_BINDING);
+  }
+
   TexCoordPrecision tex_coord_precision = TexCoordPrecisionRequired(
       gl_,
       &highp_threshold_cache_,
@@ -2018,7 +2287,7 @@
       draw_cache_.nearest_neighbor != quad->nearest_neighbor ||
       draw_cache_.background_color != quad->background_color ||
       draw_cache_.matrix_data.size() >= 8) {
-    FlushTextureQuadCache();
+    FlushTextureQuadCache(SHARED_BINDING);
     draw_cache_.program_id = binding.program_id;
     draw_cache_.resource_id = resource_id;
     draw_cache_.needs_blending = quad->ShouldDrawWithBlending();
@@ -2033,7 +2302,12 @@
   }
 
   // Generate the uv-transform
-  draw_cache_.uv_xform_data.push_back(UVTransform(quad));
+  if (!clip_region) {
+    draw_cache_.uv_xform_data.push_back(UVTransform(quad));
+  } else {
+    Float4 uv_transform = {{0.0f, 0.0f, 1.0f, 1.0f}};
+    draw_cache_.uv_xform_data.push_back(uv_transform);
+  }
 
   // Generate the vertex opacity
   const float opacity = quad->opacity();
@@ -2050,10 +2324,32 @@
   Float16 m;
   quad_rect_matrix.matrix().asColMajorf(m.data);
   draw_cache_.matrix_data.push_back(m);
+
+  if (clip_region) {
+    gfx::QuadF scaled_region;
+    if (!GetScaledRegion(quad->rect, clip_region, &scaled_region)) {
+      scaled_region = SharedGeometryQuad().BoundingBox();
+    }
+    // Both the scaled region and the SharedGeomtryQuad are in the space
+    // -0.5->0.5. We need to move that to the space 0->1.
+    float uv[8];
+    uv[0] = scaled_region.p1().x() + 0.5f;
+    uv[1] = scaled_region.p1().y() + 0.5f;
+    uv[2] = scaled_region.p2().x() + 0.5f;
+    uv[3] = scaled_region.p2().y() + 0.5f;
+    uv[4] = scaled_region.p3().x() + 0.5f;
+    uv[5] = scaled_region.p3().y() + 0.5f;
+    uv[6] = scaled_region.p4().x() + 0.5f;
+    uv[7] = scaled_region.p4().y() + 0.5f;
+    PrepareGeometry(CLIPPED_BINDING);
+    clipped_geometry_->InitializeCustomQuadWithUVs(scaled_region, uv);
+    FlushTextureQuadCache(CLIPPED_BINDING);
+  }
 }
 
 void GLRenderer::DrawIOSurfaceQuad(const DrawingFrame* frame,
-                                   const IOSurfaceDrawQuad* quad) {
+                                   const IOSurfaceDrawQuad* quad,
+                                   const gfx::QuadF* clip_region) {
   SetBlendEnabled(quad->ShouldDrawWithBlending());
 
   TexCoordPrecision tex_coord_precision = TexCoordPrecisionRequired(
@@ -2092,8 +2388,15 @@
   DCHECK_EQ(GL_TEXTURE0, GetActiveTextureUnit(gl_));
   GLC(gl_, gl_->BindTexture(GL_TEXTURE_RECTANGLE_ARB, lock.texture_id()));
 
-  DrawQuadGeometry(
-      frame, quad->quadTransform(), quad->rect, binding.matrix_location);
+  if (!clip_region) {
+    DrawQuadGeometry(frame, quad->quadTransform(), quad->rect,
+                     binding.matrix_location);
+  } else {
+    float uvs[8] = {0};
+    GetScaledUVs(quad->visible_rect, clip_region, uvs);
+    DrawQuadGeometryClippedByQuadF(frame, quad->quadTransform(), quad->rect,
+                                   *clip_region, binding.matrix_location, uvs);
+  }
 
   GLC(gl_, gl_->BindTexture(GL_TEXTURE_RECTANGLE_ARB, 0));
 }
@@ -2114,7 +2417,9 @@
   ScheduleOverlays(frame);
 }
 
-void GLRenderer::FinishDrawingQuadList() { FlushTextureQuadCache(); }
+void GLRenderer::FinishDrawingQuadList() {
+  FlushTextureQuadCache(SHARED_BINDING);
+}
 
 bool GLRenderer::FlippedFramebuffer(const DrawingFrame* frame) const {
   if (frame->current_render_pass != frame->root_render_pass)
@@ -2131,7 +2436,7 @@
   if (is_scissor_enabled_)
     return;
 
-  FlushTextureQuadCache();
+  FlushTextureQuadCache(SHARED_BINDING);
   GLC(gl_, gl_->Enable(GL_SCISSOR_TEST));
   is_scissor_enabled_ = true;
 }
@@ -2140,7 +2445,7 @@
   if (!is_scissor_enabled_)
     return;
 
-  FlushTextureQuadCache();
+  FlushTextureQuadCache(SHARED_BINDING);
   GLC(gl_, gl_->Disable(GL_SCISSOR_TEST));
   is_scissor_enabled_ = false;
 }
@@ -2209,10 +2514,34 @@
   program_shadow_ = program;
 }
 
+void GLRenderer::DrawQuadGeometryClippedByQuadF(
+    const DrawingFrame* frame,
+    const gfx::Transform& draw_transform,
+    const gfx::RectF& quad_rect,
+    const gfx::QuadF& clipping_region_quad,
+    int matrix_location,
+    const float* uvs) {
+  PrepareGeometry(CLIPPED_BINDING);
+  if (uvs) {
+    clipped_geometry_->InitializeCustomQuadWithUVs(clipping_region_quad, uvs);
+  } else {
+    clipped_geometry_->InitializeCustomQuad(clipping_region_quad);
+  }
+  gfx::Transform quad_rect_matrix;
+  QuadRectTransform(&quad_rect_matrix, draw_transform, quad_rect);
+  static float gl_matrix[16];
+  ToGLMatrix(&gl_matrix[0], frame->projection_matrix * quad_rect_matrix);
+  GLC(gl_, gl_->UniformMatrix4fv(matrix_location, 1, false, &gl_matrix[0]));
+
+  GLC(gl_, gl_->DrawElements(GL_TRIANGLES, 6, GL_UNSIGNED_SHORT,
+                             reinterpret_cast<const void*>(0)));
+}
+
 void GLRenderer::DrawQuadGeometry(const DrawingFrame* frame,
                                   const gfx::Transform& draw_transform,
                                   const gfx::RectF& quad_rect,
                                   int matrix_location) {
+  PrepareGeometry(SHARED_BINDING);
   gfx::Transform quad_rect_matrix;
   QuadRectTransform(&quad_rect_matrix, draw_transform, quad_rect);
   static float gl_matrix[16];
@@ -2275,6 +2604,7 @@
     output_surface_->context_provider()->DeleteCachedResources();
     GLC(gl_, gl_->Flush());
   }
+  PrepareGeometry(NO_BINDING);
 }
 
 void GLRenderer::DiscardBackbuffer() {
@@ -2606,7 +2936,7 @@
     return;
 
   scissor_rect_ = scissor_rect;
-  FlushTextureQuadCache();
+  FlushTextureQuadCache(SHARED_BINDING);
   GLC(gl_,
       gl_->Scissor(scissor_rect.x(),
                    scissor_rect.y(),
@@ -2631,8 +2961,27 @@
   // Create an FBO for doing offscreen rendering.
   GLC(gl_, gl_->GenFramebuffers(1, &offscreen_framebuffer_id_));
 
-  shared_geometry_ = make_scoped_ptr(
-      new GeometryBinding(gl_, QuadVertexRect()));
+  shared_geometry_ =
+      make_scoped_ptr(new StaticGeometryBinding(gl_, QuadVertexRect()));
+  clipped_geometry_ = make_scoped_ptr(new DynamicGeometryBinding(gl_));
+}
+
+void GLRenderer::PrepareGeometry(BoundGeometry binding) {
+  if (binding == bound_geometry_) {
+    return;
+  }
+
+  switch (binding) {
+    case SHARED_BINDING:
+      shared_geometry_->PrepareForDraw();
+      break;
+    case CLIPPED_BINDING:
+      clipped_geometry_->PrepareForDraw();
+      break;
+    case NO_BINDING:
+      break;
+  }
+  bound_geometry_ = binding;
 }
 
 const GLRenderer::TileCheckerboardProgram*
@@ -3112,8 +3461,8 @@
 
 void GLRenderer::RestoreGLState() {
   // This restores the current GLRenderer state to the GL context.
-
-  shared_geometry_->PrepareForDraw();
+  bound_geometry_ = NO_BINDING;
+  PrepareGeometry(SHARED_BINDING);
 
   GLC(gl_, gl_->Disable(GL_DEPTH_TEST));
   GLC(gl_, gl_->Disable(GL_CULL_FACE));
diff --git a/cc/output/gl_renderer.h b/cc/output/gl_renderer.h
index 01668ab..27f2c422 100644
--- a/cc/output/gl_renderer.h
+++ b/cc/output/gl_renderer.h
@@ -39,7 +39,8 @@
 class StreamVideoDrawQuad;
 class TextureDrawQuad;
 class TextureMailboxDeleter;
-class GeometryBinding;
+class StaticGeometryBinding;
+class DynamicGeometryBinding;
 class ScopedEnsureFramebufferAllocation;
 
 // Class that handles drawing of composited render layers using GL.
@@ -85,7 +86,7 @@
   bool IsBackbufferDiscarded() const { return is_backbuffer_discarded_; }
 
   const gfx::QuadF& SharedGeometryQuad() const { return shared_geometry_quad_; }
-  const GeometryBinding* SharedGeometry() const {
+  const StaticGeometryBinding* SharedGeometry() const {
     return shared_geometry_.get();
   }
 
@@ -96,7 +97,8 @@
                              ResourceFormat texture_format,
                              const gfx::Rect& device_rect);
   void ReleaseRenderPassTextures();
-
+  enum BoundGeometry { NO_BINDING, SHARED_BINDING, CLIPPED_BINDING };
+  void PrepareGeometry(BoundGeometry geometry_to_bind);
   void SetStencilEnabled(bool enabled);
   bool stencil_enabled() const { return stencil_shadow_; }
   void SetBlendEnabled(bool enabled);
@@ -112,7 +114,9 @@
                      bool draw_rect_covers_full_surface) override;
   void ClearFramebuffer(DrawingFrame* frame,
                         bool has_external_stencil_test) override;
-  void DoDrawQuad(DrawingFrame* frame, const class DrawQuad*) override;
+  void DoDrawQuad(DrawingFrame* frame,
+                  const class DrawQuad*,
+                  const gfx::QuadF* draw_region) override;
   void BeginDrawingFrame(DrawingFrame* frame) override;
   void FinishDrawingFrame(DrawingFrame* frame) override;
   bool FlippedFramebuffer(const DrawingFrame* frame) const override;
@@ -132,10 +136,13 @@
   // Inflate the quad and fill edge array for fragment shader.
   // |local_quad| is set to inflated quad. |edge| array is filled with
   // inflated quad's edge data.
-  static void SetupQuadForAntialiasing(const gfx::Transform& device_transform,
-                                       const DrawQuad* quad,
-                                       gfx::QuadF* local_quad,
-                                       float edge[24]);
+  static void SetupQuadForClippingAndAntialiasing(
+      const gfx::Transform& device_transform,
+      const DrawQuad* quad,
+      bool use_aa,
+      const gfx::QuadF* clip_region,
+      gfx::QuadF* local_quad,
+      float edge[24]);
 
  private:
   friend class GLRendererShaderPixelTest;
@@ -144,7 +151,8 @@
   static void ToGLMatrix(float* gl_matrix, const gfx::Transform& transform);
 
   void DrawCheckerboardQuad(const DrawingFrame* frame,
-                            const CheckerboardDrawQuad* quad);
+                            const CheckerboardDrawQuad* quad,
+                            const gfx::QuadF* clip_region);
   void DrawDebugBorderQuad(const DrawingFrame* frame,
                            const DebugBorderDrawQuad* quad);
   static bool IsDefaultBlendMode(SkXfermode::Mode blend_mode) {
@@ -158,6 +166,7 @@
       DrawingFrame* frame,
       const RenderPassDrawQuad* quad,
       const gfx::Transform& contents_device_transform,
+      const gfx::QuadF* clip_region,
       bool use_aa);
   scoped_ptr<ScopedResource> GetBackdropTexture(const gfx::Rect& bounding_rect);
 
@@ -168,34 +177,56 @@
       const RenderPassDrawQuad* quad,
       ScopedResource* background_texture);
 
-  void DrawRenderPassQuad(DrawingFrame* frame, const RenderPassDrawQuad* quad);
+  void DrawRenderPassQuad(DrawingFrame* frame,
+                          const RenderPassDrawQuad* quadi,
+                          const gfx::QuadF* clip_region);
   void DrawSolidColorQuad(const DrawingFrame* frame,
-                          const SolidColorDrawQuad* quad);
+                          const SolidColorDrawQuad* quad,
+                          const gfx::QuadF* clip_region);
   void DrawStreamVideoQuad(const DrawingFrame* frame,
-                           const StreamVideoDrawQuad* quad);
+                           const StreamVideoDrawQuad* quad,
+                           const gfx::QuadF* clip_region);
+  void DrawTextureQuad(const DrawingFrame* frame,
+                       const TextureDrawQuad* quad,
+                       const gfx::QuadF* clip_region);
   void EnqueueTextureQuad(const DrawingFrame* frame,
-                          const TextureDrawQuad* quad);
-  void FlushTextureQuadCache();
+                          const TextureDrawQuad* quad,
+                          const gfx::QuadF* clip_region);
+  void FlushTextureQuadCache(BoundGeometry flush_binding);
   void DrawIOSurfaceQuad(const DrawingFrame* frame,
-                         const IOSurfaceDrawQuad* quad);
-  void DrawTileQuad(const DrawingFrame* frame, const TileDrawQuad* quad);
+                         const IOSurfaceDrawQuad* quad,
+                         const gfx::QuadF* clip_region);
+  void DrawTileQuad(const DrawingFrame* frame,
+                    const TileDrawQuad* quad,
+                    const gfx::QuadF* clip_region);
   void DrawContentQuad(const DrawingFrame* frame,
                        const ContentDrawQuadBase* quad,
-                       ResourceProvider::ResourceId resource_id);
+                       ResourceProvider::ResourceId resource_id,
+                       const gfx::QuadF* clip_region);
   void DrawContentQuadAA(const DrawingFrame* frame,
                          const ContentDrawQuadBase* quad,
                          ResourceProvider::ResourceId resource_id,
-                         const gfx::Transform& device_transform);
+                         const gfx::Transform& device_transform,
+                         const gfx::QuadF* clip_region);
   void DrawContentQuadNoAA(const DrawingFrame* frame,
                            const ContentDrawQuadBase* quad,
-                           ResourceProvider::ResourceId resource_id);
+                           ResourceProvider::ResourceId resource_id,
+                           const gfx::QuadF* clip_region);
   void DrawYUVVideoQuad(const DrawingFrame* frame,
-                        const YUVVideoDrawQuad* quad);
+                        const YUVVideoDrawQuad* quad,
+                        const gfx::QuadF* clip_region);
   void DrawPictureQuad(const DrawingFrame* frame,
-                       const PictureDrawQuad* quad);
+                       const PictureDrawQuad* quad,
+                       const gfx::QuadF* clip_region);
 
   void SetShaderOpacity(float opacity, int alpha_location);
   void SetShaderQuadF(const gfx::QuadF& quad, int quad_location);
+  void DrawQuadGeometryClippedByQuadF(const DrawingFrame* frame,
+                                      const gfx::Transform& draw_transform,
+                                      const gfx::RectF& quad_rect,
+                                      const gfx::QuadF& clipping_region_quad,
+                                      int matrix_location,
+                                      const float uv[8]);
   void DrawQuadGeometry(const DrawingFrame* frame,
                         const gfx::Transform& draw_transform,
                         const gfx::RectF& quad_rect,
@@ -237,7 +268,8 @@
 
   unsigned offscreen_framebuffer_id_;
 
-  scoped_ptr<GeometryBinding> shared_geometry_;
+  scoped_ptr<StaticGeometryBinding> shared_geometry_;
+  scoped_ptr<DynamicGeometryBinding> clipped_geometry_;
   gfx::QuadF shared_geometry_quad_;
 
   // This block of bindings defines all of the programs used by the compositor
@@ -480,7 +512,7 @@
 
   SkBitmap on_demand_tile_raster_bitmap_;
   ResourceProvider::ResourceId on_demand_tile_raster_resource_id_;
-
+  BoundGeometry bound_geometry_;
   DISALLOW_COPY_AND_ASSIGN(GLRenderer);
 };
 
diff --git a/cc/output/overlay_unittest.cc b/cc/output/overlay_unittest.cc
index 5590ec3d..9090621 100644
--- a/cc/output/overlay_unittest.cc
+++ b/cc/output/overlay_unittest.cc
@@ -775,7 +775,10 @@
                    0),
         expect_overlays_(false) {}
 
-  MOCK_METHOD2(DoDrawQuad, void(DrawingFrame* frame, const DrawQuad* quad));
+  MOCK_METHOD3(DoDrawQuad,
+               void(DrawingFrame* frame,
+                    const DrawQuad* quad,
+                    const gfx::QuadF* draw_region));
 
   using GLRenderer::BeginDrawingFrame;
 
@@ -875,7 +878,7 @@
 
   // Candidate pass was taken out and extra skipped pass added,
   // so only draw 2 quads.
-  EXPECT_CALL(*renderer_, DoDrawQuad(_, _)).Times(2);
+  EXPECT_CALL(*renderer_, DoDrawQuad(_, _, _)).Times(2);
   EXPECT_CALL(scheduler_,
               Schedule(1,
                        gfx::OVERLAY_TRANSFORM_NONE,
@@ -913,7 +916,7 @@
   pass_list.push_back(pass.Pass());
 
   // 3 quads in the pass, all should draw.
-  EXPECT_CALL(*renderer_, DoDrawQuad(_, _)).Times(3);
+  EXPECT_CALL(*renderer_, DoDrawQuad(_, _, _)).Times(3);
   EXPECT_CALL(scheduler_, Schedule(_, _, _, _, _)).Times(0);
   renderer_->DrawFrame(&pass_list, 1.f, viewport_rect, viewport_rect, false);
 
@@ -946,7 +949,7 @@
   pass_list.push_back(pass.Pass());
 
   // Should see no overlays.
-  EXPECT_CALL(*renderer_, DoDrawQuad(_, _)).Times(3);
+  EXPECT_CALL(*renderer_, DoDrawQuad(_, _, _)).Times(3);
   EXPECT_CALL(scheduler_, Schedule(_, _, _, _, _)).Times(0);
   renderer_->DrawFrame(&pass_list, 1.f, viewport_rect, viewport_rect, false);
 
diff --git a/cc/output/renderer_pixeltest.cc b/cc/output/renderer_pixeltest.cc
index b30b0a5..295fc84 100644
--- a/cc/output/renderer_pixeltest.cc
+++ b/cc/output/renderer_pixeltest.cc
@@ -111,6 +111,52 @@
                FilterOperations());  // background filters
 }
 
+void CreateTestTwoColoredTextureDrawQuad(const gfx::Rect& rect,
+                                         SkColor texel_color,
+                                         SkColor texel_stripe_color,
+                                         SkColor background_color,
+                                         bool premultiplied_alpha,
+                                         const SharedQuadState* shared_state,
+                                         ResourceProvider* resource_provider,
+                                         RenderPass* render_pass) {
+  SkPMColor pixel_color = premultiplied_alpha
+                              ? SkPreMultiplyColor(texel_color)
+                              : SkPackARGB32NoCheck(SkColorGetA(texel_color),
+                                                    SkColorGetR(texel_color),
+                                                    SkColorGetG(texel_color),
+                                                    SkColorGetB(texel_color));
+  SkPMColor pixel_stripe_color =
+      premultiplied_alpha
+          ? SkPreMultiplyColor(texel_stripe_color)
+          : SkPackARGB32NoCheck(SkColorGetA(texel_stripe_color),
+                                SkColorGetR(texel_stripe_color),
+                                SkColorGetG(texel_stripe_color),
+                                SkColorGetB(texel_stripe_color));
+  std::vector<uint32_t> pixels(rect.size().GetArea(), pixel_color);
+  for (int i = rect.height() / 4; i < (rect.height() * 3 / 4); ++i) {
+    for (int k = rect.width() / 4; k < (rect.width() * 3 / 4); ++k) {
+      pixels[i * rect.width() + k] = pixel_stripe_color;
+    }
+  }
+  ResourceProvider::ResourceId resource = resource_provider->CreateResource(
+      rect.size(), GL_CLAMP_TO_EDGE, ResourceProvider::TEXTURE_HINT_IMMUTABLE,
+      RGBA_8888);
+  resource_provider->SetPixels(resource,
+                               reinterpret_cast<uint8_t*>(&pixels.front()),
+                               rect, rect, gfx::Vector2d());
+
+  float vertex_opacity[4] = {1.0f, 1.0f, 1.0f, 1.0f};
+  const gfx::PointF uv_top_left(0.0f, 0.0f);
+  const gfx::PointF uv_bottom_right(1.0f, 1.0f);
+  const bool flipped = false;
+  const bool nearest_neighbor = false;
+  TextureDrawQuad* quad =
+      render_pass->CreateAndAppendDrawQuad<TextureDrawQuad>();
+  quad->SetNew(shared_state, rect, gfx::Rect(), rect, resource,
+               premultiplied_alpha, uv_top_left, uv_bottom_right,
+               background_color, vertex_opacity, flipped, nearest_neighbor);
+}
+
 void CreateTestTextureDrawQuad(const gfx::Rect& rect,
                                SkColor texel_color,
                                SkColor background_color,
@@ -135,20 +181,216 @@
 
   float vertex_opacity[4] = {1.0f, 1.0f, 1.0f, 1.0f};
 
+  const gfx::PointF uv_top_left(0.0f, 0.0f);
+  const gfx::PointF uv_bottom_right(1.0f, 1.0f);
+  const bool flipped = false;
+  const bool nearest_neighbor = false;
   TextureDrawQuad* quad =
       render_pass->CreateAndAppendDrawQuad<TextureDrawQuad>();
-  quad->SetNew(shared_state,
-               rect,
-               gfx::Rect(),
-               rect,
-               resource,
-               premultiplied_alpha,
-               gfx::PointF(0.0f, 0.0f),  // uv_top_left
-               gfx::PointF(1.0f, 1.0f),  // uv_bottom_right
-               background_color,
-               vertex_opacity,
-               false,   // flipped
-               false);  // nearest_neighbor
+  quad->SetNew(shared_state, rect, gfx::Rect(), rect, resource,
+               premultiplied_alpha, uv_top_left, uv_bottom_right,
+               background_color, vertex_opacity, flipped, nearest_neighbor);
+}
+
+void CreateTestYUVVideoDrawQuad_FromVideoFrame(
+    const SharedQuadState* shared_state,
+    scoped_refptr<media::VideoFrame> video_frame,
+    uint8 alpha_value,
+    const gfx::RectF& tex_coord_rect,
+    RenderPass* render_pass,
+    VideoResourceUpdater* video_resource_updater,
+    const gfx::Rect& rect,
+    ResourceProvider* resource_provider) {
+  const bool with_alpha = (video_frame->format() == media::VideoFrame::YV12A);
+  const YUVVideoDrawQuad::ColorSpace color_space =
+      (video_frame->format() == media::VideoFrame::YV12J
+           ? YUVVideoDrawQuad::JPEG
+           : YUVVideoDrawQuad::REC_601);
+  const gfx::Rect opaque_rect(0, 0, 0, 0);
+
+  if (with_alpha) {
+    memset(video_frame->data(media::VideoFrame::kAPlane), alpha_value,
+           video_frame->stride(media::VideoFrame::kAPlane) *
+               video_frame->rows(media::VideoFrame::kAPlane));
+  }
+
+  VideoFrameExternalResources resources =
+      video_resource_updater->CreateExternalResourcesFromVideoFrame(
+          video_frame);
+
+  EXPECT_EQ(VideoFrameExternalResources::YUV_RESOURCE, resources.type);
+  EXPECT_EQ(media::VideoFrame::NumPlanes(video_frame->format()),
+            resources.mailboxes.size());
+  EXPECT_EQ(media::VideoFrame::NumPlanes(video_frame->format()),
+            resources.release_callbacks.size());
+
+  ResourceProvider::ResourceId y_resource =
+      resource_provider->CreateResourceFromTextureMailbox(
+          resources.mailboxes[media::VideoFrame::kYPlane],
+          SingleReleaseCallbackImpl::Create(
+              resources.release_callbacks[media::VideoFrame::kYPlane]));
+  ResourceProvider::ResourceId u_resource =
+      resource_provider->CreateResourceFromTextureMailbox(
+          resources.mailboxes[media::VideoFrame::kUPlane],
+          SingleReleaseCallbackImpl::Create(
+              resources.release_callbacks[media::VideoFrame::kUPlane]));
+  ResourceProvider::ResourceId v_resource =
+      resource_provider->CreateResourceFromTextureMailbox(
+          resources.mailboxes[media::VideoFrame::kVPlane],
+          SingleReleaseCallbackImpl::Create(
+              resources.release_callbacks[media::VideoFrame::kVPlane]));
+  ResourceProvider::ResourceId a_resource = 0;
+  if (with_alpha) {
+    a_resource = resource_provider->CreateResourceFromTextureMailbox(
+        resources.mailboxes[media::VideoFrame::kAPlane],
+        SingleReleaseCallbackImpl::Create(
+            resources.release_callbacks[media::VideoFrame::kAPlane]));
+  }
+
+  YUVVideoDrawQuad* yuv_quad =
+      render_pass->CreateAndAppendDrawQuad<YUVVideoDrawQuad>();
+  yuv_quad->SetNew(shared_state, rect, opaque_rect, rect, tex_coord_rect,
+                   video_frame->coded_size(), y_resource, u_resource,
+                   v_resource, a_resource, color_space);
+}
+
+void CreateTestYUVVideoDrawQuad_Striped(
+    const SharedQuadState* shared_state,
+    media::VideoFrame::Format format,
+    bool is_transparent,
+    const gfx::RectF& tex_coord_rect,
+    RenderPass* render_pass,
+    VideoResourceUpdater* video_resource_updater,
+    const gfx::Rect& rect,
+    ResourceProvider* resource_provider) {
+  scoped_refptr<media::VideoFrame> video_frame = media::VideoFrame::CreateFrame(
+      format, rect.size(), rect, rect.size(), base::TimeDelta());
+
+  // YUV values representing a striped pattern, for validating texture
+  // coordinates for sampling.
+  uint8_t y_value = 0;
+  uint8_t u_value = 0;
+  uint8_t v_value = 0;
+  for (int i = 0; i < video_frame->rows(media::VideoFrame::kYPlane); ++i) {
+    uint8_t* y_row = video_frame->data(media::VideoFrame::kYPlane) +
+                     video_frame->stride(media::VideoFrame::kYPlane) * i;
+    for (int j = 0; j < video_frame->row_bytes(media::VideoFrame::kYPlane);
+         ++j) {
+      y_row[j] = (y_value += 1);
+    }
+  }
+  for (int i = 0; i < video_frame->rows(media::VideoFrame::kUPlane); ++i) {
+    uint8_t* u_row = video_frame->data(media::VideoFrame::kUPlane) +
+                     video_frame->stride(media::VideoFrame::kUPlane) * i;
+    uint8_t* v_row = video_frame->data(media::VideoFrame::kVPlane) +
+                     video_frame->stride(media::VideoFrame::kVPlane) * i;
+    for (int j = 0; j < video_frame->row_bytes(media::VideoFrame::kUPlane);
+         ++j) {
+      u_row[j] = (u_value += 3);
+      v_row[j] = (v_value += 5);
+    }
+  }
+  uint8 alpha_value = is_transparent ? 0 : 128;
+  CreateTestYUVVideoDrawQuad_FromVideoFrame(
+      shared_state, video_frame, alpha_value, tex_coord_rect, render_pass,
+      video_resource_updater, rect, resource_provider);
+}
+
+// Creates a video frame of size background_size filled with yuv_background,
+// and then draws a foreground rectangle in a different color on top of
+// that. The foreground rectangle must have coordinates that are divisible
+// by 2 because YUV is a block format.
+void CreateTestYUVVideoDrawQuad_TwoColor(
+    const SharedQuadState* shared_state,
+    media::VideoFrame::Format format,
+    bool is_transparent,
+    const gfx::RectF& tex_coord_rect,
+    const gfx::Size& background_size,
+    uint8 y_background,
+    uint8 u_background,
+    uint8 v_background,
+    const gfx::Rect& foreground_rect,
+    uint8 y_foreground,
+    uint8 u_foreground,
+    uint8 v_foreground,
+    RenderPass* render_pass,
+    VideoResourceUpdater* video_resource_updater,
+    ResourceProvider* resource_provider) {
+  const gfx::Rect rect(background_size);
+
+  scoped_refptr<media::VideoFrame> video_frame =
+      media::VideoFrame::CreateFrame(format, background_size, foreground_rect,
+                                     foreground_rect.size(), base::TimeDelta());
+
+  int planes[] = {media::VideoFrame::kYPlane,
+                  media::VideoFrame::kUPlane,
+                  media::VideoFrame::kVPlane};
+  uint8 yuv_background[] = {y_background, u_background, v_background};
+  uint8 yuv_foreground[] = {y_foreground, u_foreground, v_foreground};
+  int sample_size[] = {1, 2, 2};
+
+  for (int i = 0; i < 3; ++i) {
+    memset(video_frame->data(planes[i]), yuv_background[i],
+           video_frame->stride(planes[i]) * video_frame->rows(planes[i]));
+  }
+
+  for (int i = 0; i < 3; ++i) {
+    // Since yuv encoding uses block encoding, widths have to be divisible
+    // by the sample size in order for this function to behave properly.
+    DCHECK_EQ(foreground_rect.x() % sample_size[i], 0);
+    DCHECK_EQ(foreground_rect.y() % sample_size[i], 0);
+    DCHECK_EQ(foreground_rect.width() % sample_size[i], 0);
+    DCHECK_EQ(foreground_rect.height() % sample_size[i], 0);
+
+    gfx::Rect sample_rect(foreground_rect.x() / sample_size[i],
+                          foreground_rect.y() / sample_size[i],
+                          foreground_rect.width() / sample_size[i],
+                          foreground_rect.height() / sample_size[i]);
+    for (int y = sample_rect.y(); y < sample_rect.bottom(); ++y) {
+      for (int x = sample_rect.x(); x < sample_rect.right(); ++x) {
+        size_t offset = y * video_frame->stride(planes[i]) + x;
+        video_frame->data(planes[i])[offset] = yuv_foreground[i];
+      }
+    }
+  }
+
+  uint8 alpha_value = 255;
+  CreateTestYUVVideoDrawQuad_FromVideoFrame(
+      shared_state, video_frame, alpha_value, tex_coord_rect, render_pass,
+      video_resource_updater, rect, resource_provider);
+}
+
+void CreateTestYUVVideoDrawQuad_Solid(
+    const SharedQuadState* shared_state,
+    media::VideoFrame::Format format,
+    bool is_transparent,
+    const gfx::RectF& tex_coord_rect,
+    uint8 y,
+    uint8 u,
+    uint8 v,
+    RenderPass* render_pass,
+    VideoResourceUpdater* video_resource_updater,
+    const gfx::Rect& rect,
+    ResourceProvider* resource_provider) {
+  scoped_refptr<media::VideoFrame> video_frame = media::VideoFrame::CreateFrame(
+      format, rect.size(), rect, rect.size(), base::TimeDelta());
+
+  // YUV values of a solid, constant, color. Useful for testing that color
+  // space/color range are being handled properly.
+  memset(video_frame->data(media::VideoFrame::kYPlane), y,
+         video_frame->stride(media::VideoFrame::kYPlane) *
+             video_frame->rows(media::VideoFrame::kYPlane));
+  memset(video_frame->data(media::VideoFrame::kUPlane), u,
+         video_frame->stride(media::VideoFrame::kUPlane) *
+             video_frame->rows(media::VideoFrame::kUPlane));
+  memset(video_frame->data(media::VideoFrame::kVPlane), v,
+         video_frame->stride(media::VideoFrame::kVPlane) *
+             video_frame->rows(media::VideoFrame::kVPlane));
+
+  uint8 alpha_value = is_transparent ? 0 : 128;
+  CreateTestYUVVideoDrawQuad_FromVideoFrame(
+      shared_state, video_frame, alpha_value, tex_coord_rect, render_pass,
+      video_resource_updater, rect, resource_provider);
 }
 
 typedef ::testing::Types<GLRenderer,
@@ -323,6 +565,278 @@
       FuzzyPixelOffByOneComparator(true)));
 }
 
+template <typename QuadType>
+static const base::FilePath::CharType* IntersectingQuadImage() {
+  return FILE_PATH_LITERAL("intersecting_blue_green_squares.png");
+}
+template <>
+const base::FilePath::CharType* IntersectingQuadImage<SolidColorDrawQuad>() {
+  return FILE_PATH_LITERAL("intersecting_blue_green.png");
+}
+template <>
+const base::FilePath::CharType* IntersectingQuadImage<YUVVideoDrawQuad>() {
+  return FILE_PATH_LITERAL("intersecting_blue_green_squares_video.png");
+}
+
+template <typename TypeParam>
+class IntersectingQuadPixelTest : public RendererPixelTest<TypeParam> {
+ protected:
+  void SetupQuadStateAndRenderPass() {
+    // This sets up a pair of draw quads. They are both rotated
+    // relative to the root plane, they are also rotated relative to each other.
+    // The intersect in the middle at a non-perpendicular angle so that any
+    // errors are hopefully magnified.
+    // The quads should intersect correctly, as in the front quad should only
+    // be partially in front of the back quad, and partially behind.
+
+    viewport_rect_ = gfx::Rect(this->device_viewport_size_);
+    quad_rect_ = gfx::Rect(0, 0, this->device_viewport_size_.width(),
+                           this->device_viewport_size_.height() / 2.0);
+
+    RenderPassId id(1, 1);
+    render_pass_ = CreateTestRootRenderPass(id, viewport_rect_);
+
+    // Create the front quad rotated on the Z and Y axis.
+    gfx::Transform trans;
+    trans.Translate3d(0, 0, 0.707 * this->device_viewport_size_.width() / 2.0);
+    trans.RotateAboutZAxis(45.0);
+    trans.RotateAboutYAxis(45.0);
+    front_quad_state_ =
+        CreateTestSharedQuadState(trans, viewport_rect_, render_pass_.get());
+    front_quad_state_->clip_rect = quad_rect_;
+    // Make sure they end up in a 3d sorting context.
+    front_quad_state_->sorting_context_id = 1;
+
+    // Create the back quad, and rotate on just the y axis. This will intersect
+    // the first quad partially.
+    trans = gfx::Transform();
+    trans.Translate3d(0, 0, -0.707 * this->device_viewport_size_.width() / 2.0);
+    trans.RotateAboutYAxis(-45.0);
+    back_quad_state_ =
+        CreateTestSharedQuadState(trans, viewport_rect_, render_pass_.get());
+    back_quad_state_->sorting_context_id = 1;
+    back_quad_state_->clip_rect = quad_rect_;
+  }
+  template <typename T>
+  void AppendBackgroundAndRunTest(const PixelComparator& comparator) {
+    SharedQuadState* background_quad_state = CreateTestSharedQuadState(
+        gfx::Transform(), viewport_rect_, render_pass_.get());
+    SolidColorDrawQuad* background_quad =
+        render_pass_->CreateAndAppendDrawQuad<SolidColorDrawQuad>();
+    background_quad->SetNew(background_quad_state, viewport_rect_,
+                            viewport_rect_, SK_ColorWHITE, false);
+    pass_list_.push_back(render_pass_.Pass());
+    const base::FilePath::CharType* fileName = IntersectingQuadImage<T>();
+    EXPECT_TRUE(
+        this->RunPixelTest(&pass_list_, base::FilePath(fileName), comparator));
+  }
+  template <typename T>
+  T* CreateAndAppendDrawQuad() {
+    return render_pass_->CreateAndAppendDrawQuad<T>();
+  }
+
+  scoped_ptr<RenderPass> render_pass_;
+  gfx::Rect viewport_rect_;
+  SharedQuadState* front_quad_state_;
+  SharedQuadState* back_quad_state_;
+  gfx::Rect quad_rect_;
+  RenderPassList pass_list_;
+};
+
+template <typename TypeParam>
+class IntersectingQuadGLPixelTest
+    : public IntersectingQuadPixelTest<TypeParam> {
+ public:
+  void SetUp() override {
+    IntersectingQuadPixelTest<TypeParam>::SetUp();
+    video_resource_updater_.reset(
+        new VideoResourceUpdater(this->output_surface_->context_provider(),
+                                 this->resource_provider_.get()));
+    video_resource_updater2_.reset(
+        new VideoResourceUpdater(this->output_surface_->context_provider(),
+                                 this->resource_provider_.get()));
+  }
+
+ protected:
+  scoped_ptr<VideoResourceUpdater> video_resource_updater_;
+  scoped_ptr<VideoResourceUpdater> video_resource_updater2_;
+};
+
+template <typename TypeParam>
+class IntersectingQuadSoftwareTest
+    : public IntersectingQuadPixelTest<TypeParam> {};
+
+typedef ::testing::Types<SoftwareRenderer, SoftwareRendererWithExpandedViewport>
+    SoftwareRendererTypes;
+typedef ::testing::Types<GLRenderer, GLRendererWithExpandedViewport>
+    GLRendererTypes;
+
+TYPED_TEST_CASE(IntersectingQuadPixelTest, RendererTypes);
+TYPED_TEST_CASE(IntersectingQuadGLPixelTest, GLRendererTypes);
+TYPED_TEST_CASE(IntersectingQuadSoftwareTest, SoftwareRendererTypes);
+
+TYPED_TEST(IntersectingQuadPixelTest, SolidColorQuads) {
+  this->SetupQuadStateAndRenderPass();
+
+  SolidColorDrawQuad* quad =
+      this->template CreateAndAppendDrawQuad<SolidColorDrawQuad>();
+  SolidColorDrawQuad* quad2 =
+      this->template CreateAndAppendDrawQuad<SolidColorDrawQuad>();
+
+  quad->SetNew(this->front_quad_state_, this->quad_rect_, this->quad_rect_,
+               SK_ColorBLUE, false);
+  quad2->SetNew(this->back_quad_state_, this->quad_rect_, this->quad_rect_,
+                SK_ColorGREEN, false);
+  SCOPED_TRACE("IntersectingSolidColorQuads");
+  this->template AppendBackgroundAndRunTest<SolidColorDrawQuad>(
+      FuzzyPixelComparator(false, 2.f, 0.f, 256.f, 256, 0.f));
+}
+
+template <typename TypeParam>
+SkColor GetColor(const SkColor& color) {
+  return color;
+}
+
+template <>
+SkColor GetColor<GLRenderer>(const SkColor& color) {
+  return SkColorSetARGB(SkColorGetA(color), SkColorGetB(color),
+                        SkColorGetG(color), SkColorGetR(color));
+}
+template <>
+SkColor GetColor<GLRendererWithExpandedViewport>(const SkColor& color) {
+  return GetColor<GLRenderer>(color);
+}
+
+TYPED_TEST(IntersectingQuadPixelTest, TexturedQuads) {
+  this->SetupQuadStateAndRenderPass();
+  CreateTestTwoColoredTextureDrawQuad(
+      this->quad_rect_, GetColor<TypeParam>(SkColorSetARGB(255, 0, 0, 0)),
+      GetColor<TypeParam>(SkColorSetARGB(255, 0, 0, 255)), SK_ColorTRANSPARENT,
+      true, this->front_quad_state_, this->resource_provider_.get(),
+      this->render_pass_.get());
+  CreateTestTwoColoredTextureDrawQuad(
+      this->quad_rect_, GetColor<TypeParam>(SkColorSetARGB(255, 0, 255, 0)),
+      GetColor<TypeParam>(SkColorSetARGB(255, 0, 0, 0)), SK_ColorTRANSPARENT,
+      true, this->back_quad_state_, this->resource_provider_.get(),
+      this->render_pass_.get());
+
+  SCOPED_TRACE("IntersectingTexturedQuads");
+  this->template AppendBackgroundAndRunTest<TextureDrawQuad>(
+      FuzzyPixelComparator(false, 2.f, 0.f, 256.f, 256, 0.f));
+}
+
+TYPED_TEST(IntersectingQuadSoftwareTest, PictureQuads) {
+  this->SetupQuadStateAndRenderPass();
+  gfx::RectF outer_rect(this->quad_rect_);
+  gfx::RectF inner_rect(this->quad_rect_.x() + (this->quad_rect_.width() / 4),
+                        this->quad_rect_.y() + (this->quad_rect_.height() / 4),
+                        this->quad_rect_.width() / 2,
+                        this->quad_rect_.height() / 2);
+
+  SkPaint black_paint;
+  black_paint.setColor(SK_ColorBLACK);
+  SkPaint blue_paint;
+  blue_paint.setColor(SK_ColorBLUE);
+  SkPaint green_paint;
+  green_paint.setColor(SK_ColorGREEN);
+
+  scoped_ptr<FakePicturePile> blue_recording =
+      FakePicturePile::CreateFilledPile(gfx::Size(1000, 1000),
+                                        this->quad_rect_.size());
+  blue_recording->add_draw_rect_with_paint(outer_rect, black_paint);
+  blue_recording->add_draw_rect_with_paint(inner_rect, blue_paint);
+  blue_recording->RerecordPile();
+  scoped_refptr<FakePicturePileImpl> blue_pile =
+      FakePicturePileImpl::CreateFromPile(blue_recording.get(), nullptr);
+
+  PictureDrawQuad* blue_quad =
+      this->render_pass_->template CreateAndAppendDrawQuad<PictureDrawQuad>();
+
+  blue_quad->SetNew(this->front_quad_state_, this->quad_rect_, gfx::Rect(),
+                    this->quad_rect_, this->quad_rect_, this->quad_rect_.size(),
+                    false, RGBA_8888, this->quad_rect_, 1.f, blue_pile);
+
+  scoped_ptr<FakePicturePile> green_recording =
+      FakePicturePile::CreateFilledPile(this->quad_rect_.size(),
+                                        this->quad_rect_.size());
+  green_recording->add_draw_rect_with_paint(outer_rect, green_paint);
+  green_recording->add_draw_rect_with_paint(inner_rect, black_paint);
+  green_recording->RerecordPile();
+  scoped_refptr<FakePicturePileImpl> green_pile =
+      FakePicturePileImpl::CreateFromPile(green_recording.get(), nullptr);
+
+  PictureDrawQuad* green_quad =
+      this->render_pass_->template CreateAndAppendDrawQuad<PictureDrawQuad>();
+  green_quad->SetNew(this->back_quad_state_, this->quad_rect_, gfx::Rect(),
+                     this->quad_rect_, this->quad_rect_,
+                     this->quad_rect_.size(), false, RGBA_8888,
+                     this->quad_rect_, 1.f, green_pile);
+  SCOPED_TRACE("IntersectingPictureQuadsPass");
+  this->template AppendBackgroundAndRunTest<PictureDrawQuad>(
+      FuzzyPixelComparator(false, 2.f, 0.f, 256.f, 256, 0.f));
+}
+
+TYPED_TEST(IntersectingQuadPixelTest, RenderPassQuads) {
+  this->SetupQuadStateAndRenderPass();
+  RenderPassId child_pass_id1(2, 2);
+  RenderPassId child_pass_id2(2, 3);
+  scoped_ptr<RenderPass> child_pass1 =
+      CreateTestRenderPass(child_pass_id1, this->quad_rect_, gfx::Transform());
+  SharedQuadState* child1_quad_state = CreateTestSharedQuadState(
+      gfx::Transform(), this->quad_rect_, child_pass1.get());
+  scoped_ptr<RenderPass> child_pass2 =
+      CreateTestRenderPass(child_pass_id2, this->quad_rect_, gfx::Transform());
+  SharedQuadState* child2_quad_state = CreateTestSharedQuadState(
+      gfx::Transform(), this->quad_rect_, child_pass2.get());
+
+  CreateTestTwoColoredTextureDrawQuad(
+      this->quad_rect_, GetColor<TypeParam>(SkColorSetARGB(255, 0, 0, 0)),
+      GetColor<TypeParam>(SkColorSetARGB(255, 0, 0, 255)), SK_ColorTRANSPARENT,
+      true, child1_quad_state, this->resource_provider_.get(),
+      child_pass1.get());
+  CreateTestTwoColoredTextureDrawQuad(
+      this->quad_rect_, GetColor<TypeParam>(SkColorSetARGB(255, 0, 255, 0)),
+      GetColor<TypeParam>(SkColorSetARGB(255, 0, 0, 0)), SK_ColorTRANSPARENT,
+      true, child2_quad_state, this->resource_provider_.get(),
+      child_pass2.get());
+
+  CreateTestRenderPassDrawQuad(this->front_quad_state_, this->quad_rect_,
+                               child_pass_id1, this->render_pass_.get());
+  CreateTestRenderPassDrawQuad(this->back_quad_state_, this->quad_rect_,
+                               child_pass_id2, this->render_pass_.get());
+
+  this->pass_list_.push_back(child_pass1.Pass());
+  this->pass_list_.push_back(child_pass2.Pass());
+  SCOPED_TRACE("IntersectingRenderQuadsPass");
+  this->template AppendBackgroundAndRunTest<RenderPassDrawQuad>(
+      FuzzyPixelComparator(false, 2.f, 0.f, 256.f, 256, 0.f));
+}
+
+TYPED_TEST(IntersectingQuadGLPixelTest, YUVVideoQuads) {
+  this->SetupQuadStateAndRenderPass();
+  gfx::Rect inner_rect(
+      ((this->quad_rect_.x() + (this->quad_rect_.width() / 4)) & ~0xF),
+      ((this->quad_rect_.y() + (this->quad_rect_.height() / 4)) & ~0xF),
+      (this->quad_rect_.width() / 2) & ~0xF,
+      (this->quad_rect_.height() / 2) & ~0xF);
+
+  CreateTestYUVVideoDrawQuad_TwoColor(
+      this->front_quad_state_, media::VideoFrame::YV12J, false,
+      gfx::RectF(0.0f, 0.0f, 1.0f, 1.0f), this->quad_rect_.size(), 0, 128, 128,
+      inner_rect, 29, 255, 107, this->render_pass_.get(),
+      this->video_resource_updater_.get(), this->resource_provider_.get());
+
+  CreateTestYUVVideoDrawQuad_TwoColor(
+      this->back_quad_state_, media::VideoFrame::YV12J, false,
+      gfx::RectF(0.0f, 0.0f, 1.0f, 1.0f), this->quad_rect_.size(), 149, 43, 21,
+      inner_rect, 0, 128, 128, this->render_pass_.get(),
+      this->video_resource_updater2_.get(), this->resource_provider_.get());
+
+  SCOPED_TRACE("IntersectingVideoQuads");
+  this->template AppendBackgroundAndRunTest<YUVVideoDrawQuad>(
+      FuzzyPixelOffByOneComparator(false));
+}
+
 // TODO(skaslev): The software renderer does not support non-premultplied alpha.
 TEST_F(GLRendererPixelTest, NonPremultipliedTextureWithoutBackground) {
   gfx::Rect rect(this->device_viewport_size_);
@@ -390,80 +904,6 @@
 
 class VideoGLRendererPixelTest : public GLRendererPixelTest {
  protected:
-  void CreateTestYUVVideoDrawQuad_Striped(const SharedQuadState* shared_state,
-                                          media::VideoFrame::Format format,
-                                          bool is_transparent,
-                                          const gfx::RectF& tex_coord_rect,
-                                          RenderPass* render_pass) {
-    const gfx::Rect rect(this->device_viewport_size_);
-
-    scoped_refptr<media::VideoFrame> video_frame =
-        media::VideoFrame::CreateFrame(
-            format, rect.size(), rect, rect.size(), base::TimeDelta());
-
-    // YUV values representing a striped pattern, for validating texture
-    // coordinates for sampling.
-    uint8_t y_value = 0;
-    uint8_t u_value = 0;
-    uint8_t v_value = 0;
-    for (int i = 0; i < video_frame->rows(media::VideoFrame::kYPlane); ++i) {
-      uint8_t* y_row = video_frame->data(media::VideoFrame::kYPlane) +
-                       video_frame->stride(media::VideoFrame::kYPlane) * i;
-      for (int j = 0; j < video_frame->row_bytes(media::VideoFrame::kYPlane);
-           ++j) {
-        y_row[j] = (y_value += 1);
-      }
-    }
-    for (int i = 0; i < video_frame->rows(media::VideoFrame::kUPlane); ++i) {
-      uint8_t* u_row = video_frame->data(media::VideoFrame::kUPlane) +
-                       video_frame->stride(media::VideoFrame::kUPlane) * i;
-      uint8_t* v_row = video_frame->data(media::VideoFrame::kVPlane) +
-                       video_frame->stride(media::VideoFrame::kVPlane) * i;
-      for (int j = 0; j < video_frame->row_bytes(media::VideoFrame::kUPlane);
-           ++j) {
-        u_row[j] = (u_value += 3);
-        v_row[j] = (v_value += 5);
-      }
-    }
-    uint8 alpha_value = is_transparent ? 0 : 128;
-    CreateTestYUVVideoDrawQuad_FromVideoFrame(
-        shared_state, video_frame, alpha_value, tex_coord_rect, render_pass);
-  }
-
-  void CreateTestYUVVideoDrawQuad_Solid(const SharedQuadState* shared_state,
-                                        media::VideoFrame::Format format,
-                                        bool is_transparent,
-                                        const gfx::RectF& tex_coord_rect,
-                                        uint8 y,
-                                        uint8 u,
-                                        uint8 v,
-                                        RenderPass* render_pass) {
-    const gfx::Rect rect(this->device_viewport_size_);
-
-    scoped_refptr<media::VideoFrame> video_frame =
-        media::VideoFrame::CreateFrame(
-            format, rect.size(), rect, rect.size(), base::TimeDelta());
-
-    // YUV values of a solid, constant, color. Useful for testing that color
-    // space/color range are being handled properly.
-    memset(video_frame->data(media::VideoFrame::kYPlane),
-           y,
-           video_frame->stride(media::VideoFrame::kYPlane) *
-               video_frame->rows(media::VideoFrame::kYPlane));
-    memset(video_frame->data(media::VideoFrame::kUPlane),
-           u,
-           video_frame->stride(media::VideoFrame::kUPlane) *
-               video_frame->rows(media::VideoFrame::kUPlane));
-    memset(video_frame->data(media::VideoFrame::kVPlane),
-           v,
-           video_frame->stride(media::VideoFrame::kVPlane) *
-               video_frame->rows(media::VideoFrame::kVPlane));
-
-    uint8 alpha_value = is_transparent ? 0 : 128;
-    CreateTestYUVVideoDrawQuad_FromVideoFrame(
-        shared_state, video_frame, alpha_value, tex_coord_rect, render_pass);
-  }
-
   void CreateEdgeBleedPass(media::VideoFrame::Format format,
                            RenderPassList* pass_list) {
     gfx::Rect rect(200, 200);
@@ -492,139 +932,19 @@
     // green sub-rectangle that should be the only thing displayed in
     // the final image.  Bleeding will appear on all four sides of the video
     // if the tex coords are not clamped.
-    CreateTestYUVVideoDrawQuad_TwoColor(shared_state, format, false,
-                                        tex_coord_rect, background_size, 0, 0,
-                                        0, green_rect, 149, 43, 21, pass.get());
+    CreateTestYUVVideoDrawQuad_TwoColor(
+        shared_state, format, false, tex_coord_rect, background_size, 0, 0, 0,
+        green_rect, 149, 43, 21, pass.get(), video_resource_updater_.get(),
+        resource_provider_.get());
     pass_list->push_back(pass.Pass());
   }
 
-  // Creates a video frame of size background_size filled with yuv_background,
-  // and then draws a foreground rectangle in a different color on top of
-  // that. The foreground rectangle must have coordinates that are divisible
-  // by 2 because YUV is a block format.
-  void CreateTestYUVVideoDrawQuad_TwoColor(const SharedQuadState* shared_state,
-                                           media::VideoFrame::Format format,
-                                           bool is_transparent,
-                                           const gfx::RectF& tex_coord_rect,
-                                           const gfx::Size& background_size,
-                                           uint8 y_background,
-                                           uint8 u_background,
-                                           uint8 v_background,
-                                           const gfx::Rect& foreground_rect,
-                                           uint8 y_foreground,
-                                           uint8 u_foreground,
-                                           uint8 v_foreground,
-                                           RenderPass* render_pass) {
-    const gfx::Rect rect(background_size);
-
-    scoped_refptr<media::VideoFrame> video_frame =
-        media::VideoFrame::CreateFrame(format, background_size, foreground_rect,
-                                       foreground_rect.size(),
-                                       base::TimeDelta());
-
-    int planes[] = {media::VideoFrame::kYPlane,
-                    media::VideoFrame::kUPlane,
-                    media::VideoFrame::kVPlane};
-    uint8 yuv_background[] = {y_background, u_background, v_background};
-    uint8 yuv_foreground[] = {y_foreground, u_foreground, v_foreground};
-    int sample_size[] = {1, 2, 2};
-
-    for (int i = 0; i < 3; ++i) {
-      memset(video_frame->data(planes[i]), yuv_background[i],
-             video_frame->stride(planes[i]) * video_frame->rows(planes[i]));
-    }
-
-    for (int i = 0; i < 3; ++i) {
-      // Since yuv encoding uses block encoding, widths have to be divisible
-      // by the sample size in order for this function to behave properly.
-      DCHECK_EQ(foreground_rect.x() % sample_size[i], 0);
-      DCHECK_EQ(foreground_rect.y() % sample_size[i], 0);
-      DCHECK_EQ(foreground_rect.width() % sample_size[i], 0);
-      DCHECK_EQ(foreground_rect.height() % sample_size[i], 0);
-
-      gfx::Rect sample_rect(foreground_rect.x() / sample_size[i],
-                            foreground_rect.y() / sample_size[i],
-                            foreground_rect.width() / sample_size[i],
-                            foreground_rect.height() / sample_size[i]);
-      for (int y = sample_rect.y(); y < sample_rect.bottom(); ++y) {
-        for (int x = sample_rect.x(); x < sample_rect.right(); ++x) {
-          size_t offset = y * video_frame->stride(planes[i]) + x;
-          video_frame->data(planes[i])[offset] = yuv_foreground[i];
-        }
-      }
-    }
-
-    uint8 alpha_value = 255;
-    CreateTestYUVVideoDrawQuad_FromVideoFrame(
-        shared_state, video_frame, alpha_value, tex_coord_rect, render_pass);
-  }
-
-  void CreateTestYUVVideoDrawQuad_FromVideoFrame(
-      const SharedQuadState* shared_state,
-      scoped_refptr<media::VideoFrame> video_frame,
-      uint8 alpha_value,
-      const gfx::RectF& tex_coord_rect,
-      RenderPass* render_pass) {
-    const bool with_alpha = (video_frame->format() == media::VideoFrame::YV12A);
-    const YUVVideoDrawQuad::ColorSpace color_space =
-        (video_frame->format() == media::VideoFrame::YV12J
-             ? YUVVideoDrawQuad::JPEG
-             : YUVVideoDrawQuad::REC_601);
-    const gfx::Rect rect(shared_state->content_bounds);
-    const gfx::Rect opaque_rect(0, 0, 0, 0);
-
-    if (with_alpha)
-      memset(video_frame->data(media::VideoFrame::kAPlane), alpha_value,
-             video_frame->stride(media::VideoFrame::kAPlane) *
-                 video_frame->rows(media::VideoFrame::kAPlane));
-
-    VideoFrameExternalResources resources =
-        video_resource_updater_->CreateExternalResourcesFromVideoFrame(
-            video_frame);
-
-    EXPECT_EQ(VideoFrameExternalResources::YUV_RESOURCE, resources.type);
-    EXPECT_EQ(media::VideoFrame::NumPlanes(video_frame->format()),
-              resources.mailboxes.size());
-    EXPECT_EQ(media::VideoFrame::NumPlanes(video_frame->format()),
-              resources.release_callbacks.size());
-
-    ResourceProvider::ResourceId y_resource =
-        resource_provider_->CreateResourceFromTextureMailbox(
-            resources.mailboxes[media::VideoFrame::kYPlane],
-            SingleReleaseCallbackImpl::Create(
-                resources.release_callbacks[media::VideoFrame::kYPlane]));
-    ResourceProvider::ResourceId u_resource =
-        resource_provider_->CreateResourceFromTextureMailbox(
-            resources.mailboxes[media::VideoFrame::kUPlane],
-            SingleReleaseCallbackImpl::Create(
-                resources.release_callbacks[media::VideoFrame::kUPlane]));
-    ResourceProvider::ResourceId v_resource =
-        resource_provider_->CreateResourceFromTextureMailbox(
-            resources.mailboxes[media::VideoFrame::kVPlane],
-            SingleReleaseCallbackImpl::Create(
-                resources.release_callbacks[media::VideoFrame::kVPlane]));
-    ResourceProvider::ResourceId a_resource = 0;
-    if (with_alpha) {
-      a_resource = resource_provider_->CreateResourceFromTextureMailbox(
-          resources.mailboxes[media::VideoFrame::kAPlane],
-          SingleReleaseCallbackImpl::Create(
-              resources.release_callbacks[media::VideoFrame::kAPlane]));
-    }
-
-    YUVVideoDrawQuad* yuv_quad =
-        render_pass->CreateAndAppendDrawQuad<YUVVideoDrawQuad>();
-    yuv_quad->SetNew(shared_state, rect, opaque_rect, rect, tex_coord_rect,
-                     video_frame->coded_size(), y_resource, u_resource,
-                     v_resource, a_resource, color_space);
-  }
-
   void SetUp() override {
     GLRendererPixelTest::SetUp();
     video_resource_updater_.reset(new VideoResourceUpdater(
         output_surface_->context_provider(), resource_provider_.get()));
   }
 
- private:
   scoped_ptr<VideoResourceUpdater> video_resource_updater_;
 };
 
@@ -637,11 +957,10 @@
   SharedQuadState* shared_state =
       CreateTestSharedQuadState(gfx::Transform(), rect, pass.get());
 
-  CreateTestYUVVideoDrawQuad_Striped(shared_state,
-                                     media::VideoFrame::YV12,
-                                     false,
-                                     gfx::RectF(0.0f, 0.0f, 1.0f, 1.0f),
-                                     pass.get());
+  CreateTestYUVVideoDrawQuad_Striped(shared_state, media::VideoFrame::YV12,
+                                     false, gfx::RectF(0.0f, 0.0f, 1.0f, 1.0f),
+                                     pass.get(), video_resource_updater_.get(),
+                                     rect, resource_provider_.get());
 
   RenderPassList pass_list;
   pass_list.push_back(pass.Pass());
@@ -662,11 +981,10 @@
       CreateTestSharedQuadState(gfx::Transform(), rect, pass.get());
 
   // Intentionally sets frame format to I420 for testing coverage.
-  CreateTestYUVVideoDrawQuad_Striped(shared_state,
-                                     media::VideoFrame::I420,
-                                     false,
-                                     gfx::RectF(0.125f, 0.25f, 0.75f, 0.5f),
-                                     pass.get());
+  CreateTestYUVVideoDrawQuad_Striped(
+      shared_state, media::VideoFrame::I420, false,
+      gfx::RectF(0.125f, 0.25f, 0.75f, 0.5f), pass.get(),
+      video_resource_updater_.get(), rect, resource_provider_.get());
 
   RenderPassList pass_list;
   pass_list.push_back(pass.Pass());
@@ -687,14 +1005,10 @@
       CreateTestSharedQuadState(gfx::Transform(), rect, pass.get());
 
   // In MPEG color range YUV values of (15,128,128) should produce black.
-  CreateTestYUVVideoDrawQuad_Solid(shared_state,
-                                   media::VideoFrame::YV12,
-                                   false,
-                                   gfx::RectF(0.0f, 0.0f, 1.0f, 1.0f),
-                                   15,
-                                   128,
-                                   128,
-                                   pass.get());
+  CreateTestYUVVideoDrawQuad_Solid(
+      shared_state, media::VideoFrame::YV12, false,
+      gfx::RectF(0.0f, 0.0f, 1.0f, 1.0f), 15, 128, 128, pass.get(),
+      video_resource_updater_.get(), rect, resource_provider_.get());
 
   RenderPassList pass_list;
   pass_list.push_back(pass.Pass());
@@ -716,14 +1030,10 @@
       CreateTestSharedQuadState(gfx::Transform(), rect, pass.get());
 
   // YUV of (149,43,21) should be green (0,255,0) in RGB.
-  CreateTestYUVVideoDrawQuad_Solid(shared_state,
-                                   media::VideoFrame::YV12J,
-                                   false,
-                                   gfx::RectF(0.0f, 0.0f, 1.0f, 1.0f),
-                                   149,
-                                   43,
-                                   21,
-                                   pass.get());
+  CreateTestYUVVideoDrawQuad_Solid(
+      shared_state, media::VideoFrame::YV12J, false,
+      gfx::RectF(0.0f, 0.0f, 1.0f, 1.0f), 149, 43, 21, pass.get(),
+      video_resource_updater_.get(), rect, resource_provider_.get());
 
   RenderPassList pass_list;
   pass_list.push_back(pass.Pass());
@@ -761,14 +1071,10 @@
       CreateTestSharedQuadState(gfx::Transform(), rect, pass.get());
 
   // Dark grey in JPEG color range (in MPEG, this is black).
-  CreateTestYUVVideoDrawQuad_Solid(shared_state,
-                                   media::VideoFrame::YV12J,
-                                   false,
-                                   gfx::RectF(0.0f, 0.0f, 1.0f, 1.0f),
-                                   15,
-                                   128,
-                                   128,
-                                   pass.get());
+  CreateTestYUVVideoDrawQuad_Solid(
+      shared_state, media::VideoFrame::YV12J, false,
+      gfx::RectF(0.0f, 0.0f, 1.0f, 1.0f), 15, 128, 128, pass.get(),
+      video_resource_updater_.get(), rect, resource_provider_.get());
 
   RenderPassList pass_list;
   pass_list.push_back(pass.Pass());
@@ -788,11 +1094,10 @@
   SharedQuadState* shared_state =
       CreateTestSharedQuadState(gfx::Transform(), rect, pass.get());
 
-  CreateTestYUVVideoDrawQuad_Striped(shared_state,
-                                     media::VideoFrame::YV12A,
-                                     false,
-                                     gfx::RectF(0.0f, 0.0f, 1.0f, 1.0f),
-                                     pass.get());
+  CreateTestYUVVideoDrawQuad_Striped(shared_state, media::VideoFrame::YV12A,
+                                     false, gfx::RectF(0.0f, 0.0f, 1.0f, 1.0f),
+                                     pass.get(), video_resource_updater_.get(),
+                                     rect, resource_provider_.get());
 
   SolidColorDrawQuad* color_quad =
       pass->CreateAndAppendDrawQuad<SolidColorDrawQuad>();
@@ -816,11 +1121,10 @@
   SharedQuadState* shared_state =
       CreateTestSharedQuadState(gfx::Transform(), rect, pass.get());
 
-  CreateTestYUVVideoDrawQuad_Striped(shared_state,
-                                     media::VideoFrame::YV12A,
-                                     true,
-                                     gfx::RectF(0.0f, 0.0f, 1.0f, 1.0f),
-                                     pass.get());
+  CreateTestYUVVideoDrawQuad_Striped(shared_state, media::VideoFrame::YV12A,
+                                     true, gfx::RectF(0.0f, 0.0f, 1.0f, 1.0f),
+                                     pass.get(), video_resource_updater_.get(),
+                                     rect, resource_provider_.get());
 
   SolidColorDrawQuad* color_quad =
       pass->CreateAndAppendDrawQuad<SolidColorDrawQuad>();
diff --git a/cc/output/software_renderer.cc b/cc/output/software_renderer.cc
index a4b54282..19c4fa3 100644
--- a/cc/output/software_renderer.cc
+++ b/cc/output/software_renderer.cc
@@ -25,6 +25,7 @@
 #include "third_party/skia/include/core/SkColor.h"
 #include "third_party/skia/include/core/SkImageFilter.h"
 #include "third_party/skia/include/core/SkMatrix.h"
+#include "third_party/skia/include/core/SkPoint.h"
 #include "third_party/skia/include/core/SkShader.h"
 #include "third_party/skia/include/effects/SkLayerRasterizer.h"
 #include "ui/gfx/geometry/rect_conversions.h"
@@ -235,7 +236,13 @@
   return false;
 }
 
-void SoftwareRenderer::DoDrawQuad(DrawingFrame* frame, const DrawQuad* quad) {
+void SoftwareRenderer::DoDrawQuad(DrawingFrame* frame,
+                                  const DrawQuad* quad,
+                                  const gfx::QuadF* draw_region) {
+  if (draw_region) {
+    current_canvas_->save();
+  }
+
   TRACE_EVENT0("cc", "SoftwareRenderer::DoDrawQuad");
   gfx::Transform quad_rect_matrix;
   QuadRectTransform(&quad_rect_matrix, quad->quadTransform(), quad->rect);
@@ -270,9 +277,31 @@
     current_paint_.setXfermodeMode(SkXfermode::kSrc_Mode);
   }
 
+  if (draw_region) {
+    gfx::QuadF local_draw_region(*draw_region);
+    SkPath draw_region_clip_path;
+    local_draw_region -=
+        gfx::Vector2dF(quad->visible_rect.x(), quad->visible_rect.y());
+    local_draw_region.Scale(1.0f / quad->visible_rect.width(),
+                            1.0f / quad->visible_rect.height());
+    local_draw_region -= gfx::Vector2dF(0.5f, 0.5f);
+
+    SkPoint clip_points[4];
+    QuadFToSkPoints(local_draw_region, clip_points);
+    draw_region_clip_path.addPoly(clip_points, 4, true);
+
+    current_canvas_->clipPath(draw_region_clip_path, SkRegion::kIntersect_Op,
+                              false);
+  }
+
   switch (quad->material) {
     case DrawQuad::CHECKERBOARD:
-      DrawCheckerboardQuad(frame, CheckerboardDrawQuad::MaterialCast(quad));
+      // TODO(enne) For now since checkerboards shouldn't be part of a 3D
+      // context, clipping regions aren't supported so we skip drawing them
+      // if this becomes the case.
+      if (!draw_region) {
+        DrawCheckerboardQuad(frame, CheckerboardDrawQuad::MaterialCast(quad));
+      }
       break;
     case DrawQuad::DEBUG_BORDER:
       DrawDebugBorderQuad(frame, DebugBorderDrawQuad::MaterialCast(quad));
@@ -307,6 +336,9 @@
   }
 
   current_canvas_->resetMatrix();
+  if (draw_region) {
+    current_canvas_->restore();
+  }
 }
 
 void SoftwareRenderer::DrawCheckerboardQuad(const DrawingFrame* frame,
diff --git a/cc/output/software_renderer.h b/cc/output/software_renderer.h
index 99868c33f..dc8e2fb0 100644
--- a/cc/output/software_renderer.h
+++ b/cc/output/software_renderer.h
@@ -52,7 +52,10 @@
                      bool draw_rect_covers_full_surface) override;
   void ClearFramebuffer(DrawingFrame* frame,
                         bool has_external_stencil_test) override;
-  void DoDrawQuad(DrawingFrame* frame, const DrawQuad* quad) override;
+
+  void DoDrawQuad(DrawingFrame* frame,
+                  const DrawQuad* quad,
+                  const gfx::QuadF* draw_region) override;
   void BeginDrawingFrame(DrawingFrame* frame) override;
   void FinishDrawingFrame(DrawingFrame* frame) override;
   bool FlippedFramebuffer(const DrawingFrame* frame) const override;
diff --git a/cc/output/static_geometry_binding.cc b/cc/output/static_geometry_binding.cc
new file mode 100644
index 0000000..683db2dd
--- /dev/null
+++ b/cc/output/static_geometry_binding.cc
@@ -0,0 +1,72 @@
+// Copyright 2015 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "cc/output/static_geometry_binding.h"
+
+#include "cc/output/gl_renderer.h"  // For the GLC() macro.
+#include "gpu/command_buffer/client/gles2_interface.h"
+#include "ui/gfx/geometry/rect_f.h"
+
+namespace cc {
+
+StaticGeometryBinding::StaticGeometryBinding(gpu::gles2::GLES2Interface* gl,
+                                             const gfx::RectF& quad_vertex_rect)
+    : gl_(gl), quad_vertices_vbo_(0), quad_elements_vbo_(0) {
+  GeometryBindingQuad quads[8];
+  GeometryBindingQuadIndex quad_indices[8];
+
+  static_assert(sizeof(GeometryBindingQuad) == 24 * sizeof(float),
+                "struct Quad should be densely packed");
+  static_assert(sizeof(GeometryBindingQuadIndex) == 6 * sizeof(uint16_t),
+                "struct QuadIndex should be densely packed");
+
+  for (size_t i = 0; i < 8; i++) {
+    GeometryBindingVertex v0 = {
+        {quad_vertex_rect.x(), quad_vertex_rect.bottom(), 0.0f},
+        {0.0f, 1.0f},
+        i * 4.0f + 0.0f};
+    GeometryBindingVertex v1 = {
+        {quad_vertex_rect.x(), quad_vertex_rect.y(), 0.0f},
+        {0.0f, 0.0f},
+        i * 4.0f + 1.0f};
+    GeometryBindingVertex v2 = {
+        {quad_vertex_rect.right(), quad_vertex_rect.y(), 0.0f},
+        {1.0f, 0.0f},
+        i * 4.0f + 2.0f};
+    GeometryBindingVertex v3 = {
+        {quad_vertex_rect.right(), quad_vertex_rect.bottom(), 0.0f},
+        {1.0f, 1.0f},
+        i * 4.0f + 3.0f};
+    GeometryBindingQuad x(v0, v1, v2, v3);
+    quads[i] = x;
+    GeometryBindingQuadIndex y(
+        static_cast<uint16>(0 + 4 * i), static_cast<uint16>(1 + 4 * i),
+        static_cast<uint16>(2 + 4 * i), static_cast<uint16>(3 + 4 * i),
+        static_cast<uint16>(0 + 4 * i), static_cast<uint16>(2 + 4 * i));
+    quad_indices[i] = y;
+  }
+
+  GLC(gl_, gl_->GenBuffers(1, &quad_vertices_vbo_));
+  GLC(gl_, gl_->GenBuffers(1, &quad_elements_vbo_));
+
+  GLC(gl_, gl_->BindBuffer(GL_ARRAY_BUFFER, quad_vertices_vbo_));
+  GLC(gl_, gl_->BufferData(GL_ARRAY_BUFFER, sizeof(GeometryBindingQuad) * 8,
+                           quads, GL_STATIC_DRAW));
+
+  GLC(gl_, gl_->BindBuffer(GL_ELEMENT_ARRAY_BUFFER, quad_elements_vbo_));
+  GLC(gl_, gl_->BufferData(GL_ELEMENT_ARRAY_BUFFER,
+                           sizeof(GeometryBindingQuadIndex) * 8, &quad_indices,
+                           GL_STATIC_DRAW));
+}
+
+StaticGeometryBinding::~StaticGeometryBinding() {
+  gl_->DeleteBuffers(1, &quad_vertices_vbo_);
+  gl_->DeleteBuffers(1, &quad_elements_vbo_);
+}
+
+void StaticGeometryBinding::PrepareForDraw() {
+  SetupGLContext(gl_, quad_elements_vbo_, quad_vertices_vbo_);
+}
+
+}  // namespace cc
diff --git a/cc/output/static_geometry_binding.h b/cc/output/static_geometry_binding.h
new file mode 100644
index 0000000..bd6bfccc
--- /dev/null
+++ b/cc/output/static_geometry_binding.h
@@ -0,0 +1,33 @@
+// Copyright 2015 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef CC_OUTPUT_STATIC_GEOMETRY_BINDING_H_
+#define CC_OUTPUT_STATIC_GEOMETRY_BINDING_H_
+
+#include "cc/output/geometry_binding.h"
+
+using gpu::gles2::GLES2Interface;
+
+namespace cc {
+
+class StaticGeometryBinding {
+ public:
+  StaticGeometryBinding(gpu::gles2::GLES2Interface* gl,
+                        const gfx::RectF& quad_vertex_rect);
+  ~StaticGeometryBinding();
+
+  void PrepareForDraw();
+
+ private:
+  gpu::gles2::GLES2Interface* gl_;
+
+  GLuint quad_vertices_vbo_;
+  GLuint quad_elements_vbo_;
+
+  DISALLOW_COPY_AND_ASSIGN(StaticGeometryBinding);
+};
+
+}  // namespace cc
+
+#endif  // CC_OUTPUT_STATIC_GEOMETRY_BINDING_H_
diff --git a/cc/quads/draw_polygon.cc b/cc/quads/draw_polygon.cc
index 026be2c..6544b64 100644
--- a/cc/quads/draw_polygon.cc
+++ b/cc/quads/draw_polygon.cc
@@ -12,7 +12,7 @@
 namespace {
 // This allows for some imperfection in the normal comparison when checking if
 // two pieces of geometry are coplanar.
-static const float coplanar_dot_epsilon = 0.01f;
+static const float coplanar_dot_epsilon = 0.001f;
 // This threshold controls how "thick" a plane is. If a point's distance is
 // <= |compare_threshold|, then it is considered on the plane. Only when this
 // boundary is crossed do we consider doing splitting.
@@ -25,21 +25,23 @@
 // points that SHOULD be intersecting the "thick plane", but actually fail to
 // test positively for it because |split_threshold| allowed them to be outside
 // this range.
+// This is really supposd to be compare_threshold / 2.0f, but that would
+// create another static initializer.
 static const float split_threshold = 0.5f;
+
+static const float normalized_threshold = 0.001f;
 }  // namespace
 
 namespace cc {
 
-gfx::Vector3dF DrawPolygon::default_normal = gfx::Vector3dF(0.0f, 0.0f, -1.0f);
-
 DrawPolygon::DrawPolygon() {
 }
 
-DrawPolygon::DrawPolygon(DrawQuad* original,
+DrawPolygon::DrawPolygon(const DrawQuad* original,
                          const std::vector<gfx::Point3F>& in_points,
                          const gfx::Vector3dF& normal,
                          int draw_order_index)
-    : order_index_(draw_order_index), original_ref_(original) {
+    : order_index_(draw_order_index), original_ref_(original), is_split_(true) {
   for (size_t i = 0; i < in_points.size(); i++) {
     points_.push_back(in_points[i]);
   }
@@ -49,12 +51,14 @@
 // This takes the original DrawQuad that this polygon should be based on,
 // a visible content rect to make the 4 corner points from, and a transformation
 // to move it and its normal into screen space.
-DrawPolygon::DrawPolygon(DrawQuad* original_ref,
+DrawPolygon::DrawPolygon(const DrawQuad* original_ref,
                          const gfx::RectF& visible_content_rect,
                          const gfx::Transform& transform,
                          int draw_order_index)
-    : order_index_(draw_order_index), original_ref_(original_ref) {
-  normal_ = default_normal;
+    : normal_(0.0f, 0.0f, 1.0f),
+      order_index_(draw_order_index),
+      original_ref_(original_ref),
+      is_split_(false) {
   gfx::Point3F points[8];
   int num_vertices_in_clipped_quad;
   gfx::QuadF send_quad(visible_content_rect);
@@ -97,6 +101,9 @@
 // Assumes that layers are split and there are no intersecting planes.
 BspCompareResult DrawPolygon::SideCompare(const DrawPolygon& a,
                                           const DrawPolygon& b) {
+  // Let's make sure that both of these are normalized.
+  DCHECK_GE(normalized_threshold, std::abs(a.normal_.LengthSquared() - 1.0f));
+  DCHECK_GE(normalized_threshold, std::abs(b.normal_.LengthSquared() - 1.0f));
   // Right away let's check if they're coplanar
   double dot = gfx::DotProduct(a.normal_, b.normal_);
   float sign = 0.0f;
@@ -105,7 +112,7 @@
   if (std::abs(dot) >= 1.0f - coplanar_dot_epsilon) {
     normal_match = true;
     // The normals are matching enough that we only have to test one point.
-    sign = gfx::DotProduct(a.points_[0] - b.points_[0], b.normal_);
+    sign = b.SignedPointDistance(a.points_[0]);
     // Is it on either side of the splitter?
     if (sign < -compare_threshold) {
       return BSP_BACK;
@@ -168,7 +175,7 @@
 
   // The case where one vertex lies on the thick-plane and the other
   // is outside of it.
-  if (std::abs(start_distance) < distance_threshold &&
+  if (std::abs(start_distance) <= distance_threshold &&
       std::abs(end_distance) > distance_threshold) {
     intersection->SetPoint(line_start.x(), line_start.y(), line_start.z());
     return true;
@@ -270,7 +277,7 @@
         break;
       }
     }
-    if (current_vertex++ > points_size) {
+    if (current_vertex++ > (points_size)) {
       break;
     }
   }
@@ -284,6 +291,7 @@
 
   // First polygon.
   out_points[0].push_back(intersections[0]);
+  DCHECK_GE(vertex_before[1], start1);
   for (size_t i = start1; i <= vertex_before[1]; i++) {
     out_points[0].push_back(points_[i]);
     --points_remaining;
@@ -306,6 +314,9 @@
   scoped_ptr<DrawPolygon> poly2(
       new DrawPolygon(original_ref_, out_points[1], normal_, order_index_));
 
+  DCHECK_GE(poly1->points().size(), 3u);
+  DCHECK_GE(poly2->points().size(), 3u);
+
   if (SideCompare(*poly1, splitter) == BSP_FRONT) {
     *front = poly1.Pass();
     *back = poly2.Pass();
diff --git a/cc/quads/draw_polygon.h b/cc/quads/draw_polygon.h
index 32643ee..b94ff1e 100644
--- a/cc/quads/draw_polygon.h
+++ b/cc/quads/draw_polygon.h
@@ -24,11 +24,11 @@
   DrawPolygon();
   ~DrawPolygon();
 
-  DrawPolygon(DrawQuad* original_ref,
+  DrawPolygon(const DrawQuad* original_ref,
               const std::vector<gfx::Point3F>& in_points,
               const gfx::Vector3dF& normal,
               int draw_order_index = 0);
-  DrawPolygon(DrawQuad* original_ref,
+  DrawPolygon(const DrawQuad* original_ref,
               const gfx::RectF& visible_content_rect,
               const gfx::Transform& transform,
               int draw_order_index = 0);
@@ -56,11 +56,9 @@
   const gfx::Vector3dF& normal() const { return normal_; }
   const DrawQuad* original_ref() const { return original_ref_; }
   int order_index() const { return order_index_; }
-
+  bool is_split() const { return is_split_; }
   scoped_ptr<DrawPolygon> CreateCopy();
 
-  static gfx::Vector3dF default_normal;
-
  private:
   void ApplyTransform(const gfx::Transform& transform);
   void ApplyTransformToNormal(const gfx::Transform& transform);
@@ -76,7 +74,8 @@
   // we need.
   // This DrawQuad is owned by the caller and its lifetime must be preserved
   // as long as this DrawPolygon is alive.
-  DrawQuad* original_ref_;
+  const DrawQuad* original_ref_;
+  bool is_split_;
 };
 
 }  // namespace cc
diff --git a/cc/quads/draw_quad.cc b/cc/quads/draw_quad.cc
index 8b8fd78b..ac1b43e 100644
--- a/cc/quads/draw_quad.cc
+++ b/cc/quads/draw_quad.cc
@@ -25,9 +25,7 @@
 namespace cc {
 
 DrawQuad::DrawQuad()
-    : material(INVALID),
-      needs_blending(false),
-      shared_quad_state() {
+    : material(INVALID), needs_blending(false), shared_quad_state(0) {
 }
 
 void DrawQuad::SetAll(const SharedQuadState* shared_quad_state,
diff --git a/cc/resources/layer_quad.cc b/cc/resources/layer_quad.cc
index 38ec7b32b..ba923149 100644
--- a/cc/resources/layer_quad.cc
+++ b/cc/resources/layer_quad.cc
@@ -10,8 +10,11 @@
 namespace cc {
 
 LayerQuad::Edge::Edge(const gfx::PointF& p, const gfx::PointF& q) {
-  DCHECK(p != q);
-
+  if (p == q) {
+    degenerate_ = true;
+    return;
+  }
+  degenerate_ = false;
   gfx::Vector2dF tangent(p.y() - q.y(), q.x() - p.x());
   float cross2 = p.x() * q.y() - q.x() * p.y();
 
@@ -43,6 +46,22 @@
       bottom_(bottom) {}
 
 gfx::QuadF LayerQuad::ToQuadF() const {
+  if (left_.degenerate()) {
+    return gfx::QuadF(top_.Intersect(bottom_), top_.Intersect(right_),
+                      right_.Intersect(bottom_), bottom_.Intersect(top_));
+  }
+  if (right_.degenerate()) {
+    return gfx::QuadF(left_.Intersect(top_), top_.Intersect(bottom_),
+                      bottom_.Intersect(top_), bottom_.Intersect(left_));
+  }
+  if (top_.degenerate()) {
+    return gfx::QuadF(left_.Intersect(right_), right_.Intersect(left_),
+                      right_.Intersect(bottom_), bottom_.Intersect(left_));
+  }
+  if (bottom_.degenerate()) {
+    return gfx::QuadF(left_.Intersect(top_), top_.Intersect(right_),
+                      right_.Intersect(left_), left_.Intersect(right_));
+  }
   return gfx::QuadF(left_.Intersect(top_),
                     top_.Intersect(right_),
                     right_.Intersect(bottom_),
@@ -50,18 +69,42 @@
 }
 
 void LayerQuad::ToFloatArray(float flattened[12]) const {
-  flattened[0] = left_.x();
-  flattened[1] = left_.y();
-  flattened[2] = left_.z();
-  flattened[3] = top_.x();
-  flattened[4] = top_.y();
-  flattened[5] = top_.z();
-  flattened[6] = right_.x();
-  flattened[7] = right_.y();
-  flattened[8] = right_.z();
-  flattened[9] = bottom_.x();
-  flattened[10] = bottom_.y();
-  flattened[11] = bottom_.z();
+  if (left_.degenerate()) {
+    flattened[0] = bottom_.x();
+    flattened[1] = bottom_.y();
+    flattened[2] = bottom_.z();
+  } else {
+    flattened[0] = left_.x();
+    flattened[1] = left_.y();
+    flattened[2] = left_.z();
+  }
+  if (top_.degenerate()) {
+    flattened[3] = left_.x();
+    flattened[4] = left_.y();
+    flattened[5] = left_.z();
+  } else {
+    flattened[3] = top_.x();
+    flattened[4] = top_.y();
+    flattened[5] = top_.z();
+  }
+  if (right_.degenerate()) {
+    flattened[6] = top_.x();
+    flattened[7] = top_.y();
+    flattened[8] = top_.z();
+  } else {
+    flattened[6] = right_.x();
+    flattened[7] = right_.y();
+    flattened[8] = right_.z();
+  }
+  if (bottom_.degenerate()) {
+    flattened[9] = right_.x();
+    flattened[10] = right_.y();
+    flattened[11] = right_.z();
+  } else {
+    flattened[9] = bottom_.x();
+    flattened[10] = bottom_.y();
+    flattened[11] = bottom_.z();
+  }
 }
 
 }  // namespace cc
diff --git a/cc/resources/layer_quad.h b/cc/resources/layer_quad.h
index ed1db10..74336453 100644
--- a/cc/resources/layer_quad.h
+++ b/cc/resources/layer_quad.h
@@ -22,7 +22,7 @@
  public:
   class Edge {
    public:
-    Edge() : x_(0), y_(0), z_(0) {}
+    Edge() : x_(0), y_(0), z_(0), degenerate_(false) {}
     Edge(const gfx::PointF& p, const gfx::PointF& q);
 
     float x() const { return x_; }
@@ -57,6 +57,8 @@
     }
     void scale(float s) { scale(s, s, s); }
 
+    bool degenerate() const { return degenerate_; }
+
     gfx::PointF Intersect(const Edge& e) const {
       return gfx::PointF(
           (y() * e.z() - e.y() * z()) / (x() * e.y() - e.x() * y()),
@@ -67,6 +69,7 @@
     float x_;
     float y_;
     float z_;
+    bool degenerate_;
   };
 
   LayerQuad(const Edge& left,
diff --git a/cc/test/data/anti_aliasing.png b/cc/test/data/anti_aliasing.png
index 4abe9e1..eba7436 100644
--- a/cc/test/data/anti_aliasing.png
+++ b/cc/test/data/anti_aliasing.png
Binary files differ
diff --git a/cc/test/data/background_filter_blur_off_axis.png b/cc/test/data/background_filter_blur_off_axis.png
index 5077171..5e9a0c2 100644
--- a/cc/test/data/background_filter_blur_off_axis.png
+++ b/cc/test/data/background_filter_blur_off_axis.png
Binary files differ
diff --git a/cc/test/data/intersecting_blue_green.png b/cc/test/data/intersecting_blue_green.png
new file mode 100644
index 0000000..8262071c
--- /dev/null
+++ b/cc/test/data/intersecting_blue_green.png
Binary files differ
diff --git a/cc/test/data/intersecting_blue_green_squares.png b/cc/test/data/intersecting_blue_green_squares.png
new file mode 100644
index 0000000..22da4d31
--- /dev/null
+++ b/cc/test/data/intersecting_blue_green_squares.png
Binary files differ
diff --git a/cc/test/data/intersecting_blue_green_squares_video.png b/cc/test/data/intersecting_blue_green_squares_video.png
new file mode 100644
index 0000000..e1bea4f
--- /dev/null
+++ b/cc/test/data/intersecting_blue_green_squares_video.png
Binary files differ
diff --git a/cc/trees/layer_sorter.cc b/cc/trees/layer_sorter.cc
deleted file mode 100644
index bde4920..0000000
--- a/cc/trees/layer_sorter.cc
+++ /dev/null
@@ -1,470 +0,0 @@
-// Copyright 2011 The Chromium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-#include "cc/trees/layer_sorter.h"
-
-#include <algorithm>
-#include <deque>
-#include <limits>
-#include <vector>
-
-#include "base/logging.h"
-#include "cc/base/math_util.h"
-#include "cc/layers/render_surface_impl.h"
-#include "ui/gfx/transform.h"
-
-namespace cc {
-
-// This epsilon is used to determine if two layers are too close to each other
-// to be able to tell which is in front of the other.  It's a relative epsilon
-// so it is robust to changes in scene scale.  This value was chosen by picking
-// a value near machine epsilon and then increasing it until the flickering on
-// the test scene went away.
-const float k_layer_epsilon = 1e-4f;
-
-inline static float PerpProduct(const gfx::Vector2dF& u,
-                                const gfx::Vector2dF& v) {
-  return u.x() * v.y() - u.y() * v.x();
-}
-
-// Tests if two edges defined by their endpoints (a,b) and (c,d) intersect.
-// Returns true and the point of intersection if they do and false otherwise.
-static bool EdgeEdgeTest(const gfx::PointF& a,
-                         const gfx::PointF& b,
-                         const gfx::PointF& c,
-                         const gfx::PointF& d,
-                         gfx::PointF* r) {
-  gfx::Vector2dF u = b - a;
-  gfx::Vector2dF v = d - c;
-  gfx::Vector2dF w = a - c;
-
-  float denom = PerpProduct(u, v);
-
-  // If denom == 0 then the edges are parallel. While they could be overlapping
-  // we don't bother to check here as the we'll find their intersections from
-  // the corner to quad tests.
-  if (!denom)
-    return false;
-
-  float s = PerpProduct(v, w) / denom;
-  if (s < 0.f || s > 1.f)
-    return false;
-
-  float t = PerpProduct(u, w) / denom;
-  if (t < 0.f || t > 1.f)
-    return false;
-
-  u.Scale(s);
-  *r = a + u;
-  return true;
-}
-
-GraphNode::GraphNode(LayerImpl* layer_impl)
-    : layer(layer_impl),
-      incoming_edge_weight(0.f) {}
-
-GraphNode::~GraphNode() {}
-
-LayerSorter::LayerSorter()
-    : z_range_(0.f) {}
-
-LayerSorter::~LayerSorter() {}
-
-static float CheckFloatingPointNumericAccuracy(float a, float b) {
-  float abs_dif = std::abs(b - a);
-  float abs_max = std::max(std::abs(b), std::abs(a));
-  // Check to see if we've got a result with a reasonable amount of error.
-  return abs_dif / abs_max;
-}
-
-// Checks whether layer "a" draws on top of layer "b". The weight value returned
-// is an indication of the maximum z-depth difference between the layers or zero
-// if the layers are found to be intesecting (some features are in front and
-// some are behind).
-LayerSorter::ABCompareResult LayerSorter::CheckOverlap(LayerShape* a,
-                                                       LayerShape* b,
-                                                       float z_threshold,
-                                                       float* weight) {
-  *weight = 0.f;
-
-  // Early out if the projected bounds don't overlap.
-  if (!a->projected_bounds.Intersects(b->projected_bounds))
-    return NONE;
-
-  gfx::PointF aPoints[4] = { a->projected_quad.p1(),
-                             a->projected_quad.p2(),
-                             a->projected_quad.p3(),
-                             a->projected_quad.p4() };
-  gfx::PointF bPoints[4] = { b->projected_quad.p1(),
-                             b->projected_quad.p2(),
-                             b->projected_quad.p3(),
-                             b->projected_quad.p4() };
-
-  // Make a list of points that inside both layer quad projections.
-  std::vector<gfx::PointF> overlap_points;
-
-  // Check all four corners of one layer against the other layer's quad.
-  for (int i = 0; i < 4; ++i) {
-    if (a->projected_quad.Contains(bPoints[i]))
-      overlap_points.push_back(bPoints[i]);
-    if (b->projected_quad.Contains(aPoints[i]))
-      overlap_points.push_back(aPoints[i]);
-  }
-
-  // Check all the edges of one layer for intersection with the other layer's
-  // edges.
-  gfx::PointF r;
-  for (int ea = 0; ea < 4; ++ea)
-    for (int eb = 0; eb < 4; ++eb)
-      if (EdgeEdgeTest(aPoints[ea], aPoints[(ea + 1) % 4],
-                       bPoints[eb], bPoints[(eb + 1) % 4],
-                       &r))
-        overlap_points.push_back(r);
-
-  if (overlap_points.empty())
-    return NONE;
-
-  // Check the corresponding layer depth value for all overlap points to
-  // determine which layer is in front.
-  float max_positive = 0.f;
-  float max_negative = 0.f;
-
-  // This flag tracks the existance of a numerically accurate seperation
-  // between two layers.  If there is no accurate seperation, the layers
-  // cannot be effectively sorted.
-  bool accurate = false;
-
-  for (size_t o = 0; o < overlap_points.size(); o++) {
-    float za = a->LayerZFromProjectedPoint(overlap_points[o]);
-    float zb = b->LayerZFromProjectedPoint(overlap_points[o]);
-
-    // Here we attempt to avoid numeric issues with layers that are too
-    // close together.  If we have 2-sided quads that are very close
-    // together then we will draw them in document order to avoid
-    // flickering.  The correct solution is for the content maker to turn
-    // on back-face culling or move the quads apart (if they're not two
-    // sides of one object).
-    if (CheckFloatingPointNumericAccuracy(za, zb) > k_layer_epsilon)
-      accurate = true;
-
-    float diff = za - zb;
-    if (diff > max_positive)
-      max_positive = diff;
-    if (diff < max_negative)
-      max_negative = diff;
-  }
-
-  // If we can't tell which should come first, we use document order.
-  if (!accurate)
-    return A_BEFORE_B;
-
-  float max_diff =
-      std::abs(max_positive) > std::abs(max_negative) ?
-          max_positive : max_negative;
-
-  // If the results are inconsistent (and the z difference substantial to rule
-  // out numerical errors) then the layers are intersecting. We will still
-  // return an order based on the maximum depth difference but with an edge
-  // weight of zero these layers will get priority if a graph cycle is present
-  // and needs to be broken.
-  if (max_positive > z_threshold && max_negative < -z_threshold)
-    *weight = 0.f;
-  else
-    *weight = std::abs(max_diff);
-
-  // Maintain relative order if the layers have the same depth at all
-  // intersection points.
-  if (max_diff <= 0.f)
-    return A_BEFORE_B;
-
-  return B_BEFORE_A;
-}
-
-LayerShape::LayerShape() {}
-
-LayerShape::LayerShape(float width,
-                       float height,
-                       const gfx::Transform& draw_transform) {
-  gfx::QuadF layer_quad(gfx::RectF(0.f, 0.f, width, height));
-
-  // Compute the projection of the layer quad onto the z = 0 plane.
-
-  gfx::PointF clipped_quad[8];
-  int num_vertices_in_clipped_quad;
-  MathUtil::MapClippedQuad(draw_transform,
-                           layer_quad,
-                           clipped_quad,
-                           &num_vertices_in_clipped_quad);
-
-  if (num_vertices_in_clipped_quad < 3) {
-    projected_bounds = gfx::RectF();
-    return;
-  }
-
-  projected_bounds =
-      MathUtil::ComputeEnclosingRectOfVertices(clipped_quad,
-                                               num_vertices_in_clipped_quad);
-
-  // NOTE: it will require very significant refactoring and overhead to deal
-  // with generalized polygons or multiple quads per layer here. For the sake of
-  // layer sorting it is equally correct to take a subsection of the polygon
-  // that can be made into a quad. This will only be incorrect in the case of
-  // intersecting layers, which are not supported yet anyway.
-  projected_quad.set_p1(clipped_quad[0]);
-  projected_quad.set_p2(clipped_quad[1]);
-  projected_quad.set_p3(clipped_quad[2]);
-  if (num_vertices_in_clipped_quad >= 4) {
-    projected_quad.set_p4(clipped_quad[3]);
-  } else {
-    // This will be a degenerate quad that is actually a triangle.
-    projected_quad.set_p4(clipped_quad[2]);
-  }
-
-  // Compute the normal of the layer's plane.
-  bool clipped = false;
-  gfx::Point3F c1 =
-      MathUtil::MapPoint(draw_transform, gfx::Point3F(0.f, 0.f, 0.f), &clipped);
-  gfx::Point3F c2 =
-      MathUtil::MapPoint(draw_transform, gfx::Point3F(0.f, 1.f, 0.f), &clipped);
-  gfx::Point3F c3 =
-      MathUtil::MapPoint(draw_transform, gfx::Point3F(1.f, 0.f, 0.f), &clipped);
-  // TODO(shawnsingh): Deal with clipping.
-  gfx::Vector3dF c12 = c2 - c1;
-  gfx::Vector3dF c13 = c3 - c1;
-  layer_normal = gfx::CrossProduct(c13, c12);
-
-  transform_origin = c1;
-}
-
-LayerShape::~LayerShape() {}
-
-// Returns the Z coordinate of a point on the layer that projects
-// to point p which lies on the z = 0 plane. It does it by computing the
-// intersection of a line starting from p along the Z axis and the plane
-// of the layer.
-float LayerShape::LayerZFromProjectedPoint(const gfx::PointF& p) const {
-  gfx::Vector3dF z_axis(0.f, 0.f, 1.f);
-  gfx::Vector3dF w = gfx::Point3F(p) - transform_origin;
-
-  float d = gfx::DotProduct(layer_normal, z_axis);
-  float n = -gfx::DotProduct(layer_normal, w);
-
-  // Check if layer is parallel to the z = 0 axis which will make it
-  // invisible and hence returning zero is fine.
-  if (!d)
-    return 0.f;
-
-  // The intersection point would be given by:
-  // p + (n / d) * u  but since we are only interested in the
-  // z coordinate and p's z coord is zero, all we need is the value of n/d.
-  return n / d;
-}
-
-void LayerSorter::CreateGraphNodes(LayerImplList::iterator first,
-                                   LayerImplList::iterator last) {
-  DVLOG(2) << "Creating graph nodes:";
-  float min_z = FLT_MAX;
-  float max_z = -FLT_MAX;
-  for (LayerImplList::const_iterator it = first; it < last; it++) {
-    nodes_.push_back(GraphNode(*it));
-    GraphNode& node = nodes_.at(nodes_.size() - 1);
-    RenderSurfaceImpl* render_surface = node.layer->render_surface();
-    if (!node.layer->DrawsContent() && !render_surface)
-      continue;
-
-    DVLOG(2) << "Layer " << node.layer->id() <<
-        " (" << node.layer->bounds().width() <<
-        " x " << node.layer->bounds().height() << ")";
-
-    gfx::Transform draw_transform;
-    float layer_width, layer_height;
-    if (render_surface) {
-      draw_transform = render_surface->draw_transform();
-      layer_width = render_surface->content_rect().width();
-      layer_height = render_surface->content_rect().height();
-    } else {
-      draw_transform = node.layer->draw_transform();
-      layer_width = node.layer->content_bounds().width();
-      layer_height = node.layer->content_bounds().height();
-    }
-
-    node.shape = LayerShape(layer_width, layer_height, draw_transform);
-
-    max_z = std::max(max_z, node.shape.transform_origin.z());
-    min_z = std::min(min_z, node.shape.transform_origin.z());
-  }
-
-  z_range_ = std::abs(max_z - min_z);
-}
-
-void LayerSorter::CreateGraphEdges() {
-  DVLOG(2) << "Edges:";
-  // Fraction of the total z_range below which z differences
-  // are not considered reliable.
-  const float z_threshold_factor = 0.01f;
-  float z_threshold = z_range_ * z_threshold_factor;
-
-  for (size_t na = 0; na < nodes_.size(); na++) {
-    GraphNode& node_a = nodes_[na];
-    if (!node_a.layer->DrawsContent() && !node_a.layer->render_surface())
-      continue;
-    for (size_t nb = na + 1; nb < nodes_.size(); nb++) {
-      GraphNode& node_b = nodes_[nb];
-      if (!node_b.layer->DrawsContent() && !node_b.layer->render_surface())
-        continue;
-      float weight = 0.f;
-      ABCompareResult overlap_result = CheckOverlap(&node_a.shape,
-                                                    &node_b.shape,
-                                                    z_threshold,
-                                                    &weight);
-      GraphNode* start_node = NULL;
-      GraphNode* end_node = NULL;
-      if (overlap_result == A_BEFORE_B) {
-        start_node = &node_a;
-        end_node = &node_b;
-      } else if (overlap_result == B_BEFORE_A) {
-        start_node = &node_b;
-        end_node = &node_a;
-      }
-
-      if (start_node) {
-        DVLOG(2) << start_node->layer->id() << " -> " << end_node->layer->id();
-        edges_.push_back(GraphEdge(start_node, end_node, weight));
-      }
-    }
-  }
-
-  for (size_t i = 0; i < edges_.size(); i++) {
-    GraphEdge& edge = edges_[i];
-    active_edges_[&edge] = &edge;
-    edge.from->outgoing.push_back(&edge);
-    edge.to->incoming.push_back(&edge);
-    edge.to->incoming_edge_weight += edge.weight;
-  }
-}
-
-// Finds and removes an edge from the list by doing a swap with the
-// last element of the list.
-void LayerSorter::RemoveEdgeFromList(GraphEdge* edge,
-                                     std::vector<GraphEdge*>* list) {
-  std::vector<GraphEdge*>::iterator iter =
-      std::find(list->begin(), list->end(), edge);
-  DCHECK(iter != list->end());
-  list->erase(iter);
-}
-
-// Sorts the given list of layers such that they can be painted in a
-// back-to-front order. Sorting produces correct results for non-intersecting
-// layers that don't have cyclical order dependencies. Cycles and intersections
-// are broken (somewhat) aribtrarily. Sorting of layers is done via a
-// topological sort of a directed graph whose nodes are the layers themselves.
-// An edge from node A to node B signifies that layer A needs to be drawn before
-// layer B. If A and B have no dependency between each other, then we preserve
-// the ordering of those layers as they were in the original list.
-//
-// The draw order between two layers is determined by projecting the two
-// triangles making up each layer quad to the Z = 0 plane, finding points of
-// intersection between the triangles and backprojecting those points to the
-// plane of the layer to determine the corresponding Z coordinate. The layer
-// with the lower Z coordinate (farther from the eye) needs to be rendered
-// first.
-//
-// If the layer projections don't intersect, then no edges (dependencies) are
-// created between them in the graph. HOWEVER, in this case we still need to
-// preserve the ordering of the original list of layers, since that list should
-// already have proper z-index ordering of layers.
-//
-void LayerSorter::Sort(LayerImplList::iterator first,
-                       LayerImplList::iterator last) {
-  DVLOG(2) << "Sorting start ----";
-  CreateGraphNodes(first, last);
-
-  CreateGraphEdges();
-
-  std::vector<GraphNode*> sorted_list;
-  std::deque<GraphNode*> no_incoming_edge_node_list;
-
-  // Find all the nodes that don't have incoming edges.
-  for (NodeList::iterator la = nodes_.begin(); la < nodes_.end(); la++) {
-    if (!la->incoming.size())
-      no_incoming_edge_node_list.push_back(&(*la));
-  }
-
-  DVLOG(2) << "Sorted list: ";
-  while (active_edges_.size() || no_incoming_edge_node_list.size()) {
-    while (no_incoming_edge_node_list.size()) {
-      // It is necessary to preserve the existing ordering of layers, when there
-      // are no explicit dependencies (because this existing ordering has
-      // correct z-index/layout ordering). To preserve this ordering, we process
-      // Nodes in the same order that they were added to the list.
-      GraphNode* from_node = no_incoming_edge_node_list.front();
-      no_incoming_edge_node_list.pop_front();
-
-      // Add it to the final list.
-      sorted_list.push_back(from_node);
-
-      DVLOG(2) << from_node->layer->id() << ", ";
-
-      // Remove all its outgoing edges from the graph.
-      for (size_t i = 0; i < from_node->outgoing.size(); i++) {
-        GraphEdge* outgoing_edge = from_node->outgoing[i];
-
-        active_edges_.erase(outgoing_edge);
-        RemoveEdgeFromList(outgoing_edge, &outgoing_edge->to->incoming);
-        outgoing_edge->to->incoming_edge_weight -= outgoing_edge->weight;
-
-        if (!outgoing_edge->to->incoming.size())
-          no_incoming_edge_node_list.push_back(outgoing_edge->to);
-      }
-      from_node->outgoing.clear();
-    }
-
-    if (!active_edges_.size())
-      break;
-
-    // If there are still active edges but the list of nodes without incoming
-    // edges is empty then we have run into a cycle. Break the cycle by finding
-    // the node with the smallest overall incoming edge weight and use it. This
-    // will favor nodes that have zero-weight incoming edges i.e. layers that
-    // are being occluded by a layer that intersects them.
-    float min_incoming_edge_weight = FLT_MAX;
-    GraphNode* next_node = NULL;
-    for (size_t i = 0; i < nodes_.size(); i++) {
-      if (nodes_[i].incoming.size() &&
-          nodes_[i].incoming_edge_weight < min_incoming_edge_weight) {
-        min_incoming_edge_weight = nodes_[i].incoming_edge_weight;
-        next_node = &nodes_[i];
-      }
-    }
-    DCHECK(next_node);
-    // Remove all its incoming edges.
-    for (size_t e = 0; e < next_node->incoming.size(); e++) {
-      GraphEdge* incoming_edge = next_node->incoming[e];
-
-      active_edges_.erase(incoming_edge);
-      RemoveEdgeFromList(incoming_edge, &incoming_edge->from->outgoing);
-    }
-    next_node->incoming.clear();
-    next_node->incoming_edge_weight = 0.f;
-    no_incoming_edge_node_list.push_back(next_node);
-    DVLOG(2) << "Breaking cycle by cleaning up incoming edges from " <<
-        next_node->layer->id() <<
-        " (weight = " << min_incoming_edge_weight << ")";
-  }
-
-  // Note: The original elements of the list are in no danger of having their
-  // ref count go to zero here as they are all nodes of the layer hierarchy and
-  // are kept alive by their parent nodes.
-  int count = 0;
-  for (LayerImplList::iterator it = first; it < last; it++)
-    *it = sorted_list[count++]->layer;
-
-  DVLOG(2) << "Sorting end ----";
-
-  nodes_.clear();
-  edges_.clear();
-  active_edges_.clear();
-}
-
-}  // namespace cc
diff --git a/cc/trees/layer_sorter.h b/cc/trees/layer_sorter.h
deleted file mode 100644
index 4cfa8fed..0000000
--- a/cc/trees/layer_sorter.h
+++ /dev/null
@@ -1,97 +0,0 @@
-// Copyright 2011 The Chromium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-#ifndef CC_TREES_LAYER_SORTER_H_
-#define CC_TREES_LAYER_SORTER_H_
-
-#include <vector>
-
-#include "base/basictypes.h"
-#include "base/containers/hash_tables.h"
-#include "cc/base/cc_export.h"
-#include "cc/layers/layer_impl.h"
-#include "ui/gfx/geometry/point3_f.h"
-#include "ui/gfx/geometry/quad_f.h"
-#include "ui/gfx/geometry/rect_f.h"
-#include "ui/gfx/geometry/vector3d_f.h"
-
-namespace gfx {
-class Transform;
-}
-
-namespace cc {
-struct GraphEdge;
-
-// Holds various useful properties derived from a layer's 3D outline.
-struct CC_EXPORT LayerShape {
-  LayerShape();
-  LayerShape(float width, float height, const gfx::Transform& draw_transform);
-  ~LayerShape();
-
-  float LayerZFromProjectedPoint(const gfx::PointF& p) const;
-
-  gfx::Vector3dF layer_normal;
-  gfx::Point3F transform_origin;
-  gfx::QuadF projected_quad;
-  gfx::RectF projected_bounds;
-};
-
-struct GraphNode {
-  explicit GraphNode(LayerImpl* layer_impl);
-  ~GraphNode();
-
-  LayerImpl* layer;
-  LayerShape shape;
-  std::vector<GraphEdge*> incoming;
-  std::vector<GraphEdge*> outgoing;
-  float incoming_edge_weight;
-};
-
-struct GraphEdge {
-  GraphEdge(GraphNode* from_node, GraphNode* to_node, float weight)
-      : from(from_node),
-        to(to_node),
-        weight(weight) {}
-
-  GraphNode* from;
-  GraphNode* to;
-  float weight;
-};
-
-
-
-class CC_EXPORT LayerSorter {
- public:
-  LayerSorter();
-  ~LayerSorter();
-
-  void Sort(LayerImplList::iterator first, LayerImplList::iterator last);
-
-  enum ABCompareResult { A_BEFORE_B, B_BEFORE_A, NONE };
-
-  static ABCompareResult CheckOverlap(LayerShape* a,
-                                      LayerShape* b,
-                                      float z_threshold,
-                                      float* weight);
-
- private:
-  typedef std::vector<GraphNode> NodeList;
-  typedef std::vector<GraphEdge> EdgeList;
-  NodeList nodes_;
-  EdgeList edges_;
-  float z_range_;
-
-  typedef base::hash_map<GraphEdge*, GraphEdge*> EdgeMap;
-  EdgeMap active_edges_;
-
-  void CreateGraphNodes(LayerImplList::iterator first,
-                        LayerImplList::iterator last);
-  void CreateGraphEdges();
-  void RemoveEdgeFromList(GraphEdge* graph, std::vector<GraphEdge*>* list);
-
-  DISALLOW_COPY_AND_ASSIGN(LayerSorter);
-};
-
-}  // namespace cc
-#endif  // CC_TREES_LAYER_SORTER_H_
diff --git a/cc/trees/layer_sorter_unittest.cc b/cc/trees/layer_sorter_unittest.cc
deleted file mode 100644
index 74d3cf5..0000000
--- a/cc/trees/layer_sorter_unittest.cc
+++ /dev/null
@@ -1,329 +0,0 @@
-// Copyright 2011 The Chromium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-#include "cc/trees/layer_sorter.h"
-
-#include "cc/base/math_util.h"
-#include "cc/layers/layer_impl.h"
-#include "cc/test/fake_impl_proxy.h"
-#include "cc/test/fake_layer_tree_host_impl.h"
-#include "cc/test/test_shared_bitmap_manager.h"
-#include "cc/trees/single_thread_proxy.h"
-#include "testing/gtest/include/gtest/gtest.h"
-#include "ui/gfx/transform.h"
-
-namespace cc {
-namespace {
-
-// Note: In the following overlap tests, the "camera" is looking down the
-// negative Z axis, meaning that layers with smaller z values (more negative)
-// are further from the camera and therefore must be drawn before layers with
-// higher z values.
-
-TEST(LayerSorterTest, BasicOverlap) {
-  LayerSorter::ABCompareResult overlap_result;
-  const float z_threshold = 0.1f;
-  float weight = 0.f;
-
-  // Trivial test, with one layer directly obscuring the other.
-  gfx::Transform neg4_translate;
-  neg4_translate.Translate3d(0.0, 0.0, -4.0);
-  LayerShape front(2.f, 2.f, neg4_translate);
-
-  gfx::Transform neg5_translate;
-  neg5_translate.Translate3d(0.0, 0.0, -5.0);
-  LayerShape back(2.f, 2.f, neg5_translate);
-
-  overlap_result =
-      LayerSorter::CheckOverlap(&front, &back, z_threshold, &weight);
-  EXPECT_EQ(LayerSorter::B_BEFORE_A, overlap_result);
-  EXPECT_EQ(1.f, weight);
-
-  overlap_result =
-      LayerSorter::CheckOverlap(&back, &front, z_threshold, &weight);
-  EXPECT_EQ(LayerSorter::A_BEFORE_B, overlap_result);
-  EXPECT_EQ(1.f, weight);
-
-  // One layer translated off to the right. No overlap should be detected.
-  gfx::Transform right_translate;
-  right_translate.Translate3d(10.0, 0.0, -5.0);
-  LayerShape back_right(2.f, 2.f, right_translate);
-  overlap_result =
-      LayerSorter::CheckOverlap(&front, &back_right, z_threshold, &weight);
-  EXPECT_EQ(LayerSorter::NONE, overlap_result);
-
-  // When comparing a layer with itself, z difference is always 0.
-  overlap_result =
-      LayerSorter::CheckOverlap(&front, &front, z_threshold, &weight);
-  EXPECT_EQ(0.f, weight);
-}
-
-TEST(LayerSorterTest, RightAngleOverlap) {
-  LayerSorter::ABCompareResult overlap_result;
-  const float z_threshold = 0.1f;
-  float weight = 0.f;
-
-  gfx::Transform perspective_matrix;
-  perspective_matrix.ApplyPerspectiveDepth(1000.0);
-
-  // Two layers forming a right angle with a perspective viewing transform.
-  gfx::Transform left_face_matrix;
-  left_face_matrix.Translate3d(-1.0, 0.0, -5.0);
-  left_face_matrix.RotateAboutYAxis(-90.0);
-  left_face_matrix.Translate(-1.0, -1.0);
-  LayerShape left_face(2.f, 2.f, perspective_matrix * left_face_matrix);
-  gfx::Transform front_face_matrix;
-  front_face_matrix.Translate3d(0.0, 0.0, -4.0);
-  front_face_matrix.Translate(-1.0, -1.0);
-  LayerShape front_face(2.f, 2.f, perspective_matrix * front_face_matrix);
-
-  overlap_result =
-      LayerSorter::CheckOverlap(&front_face, &left_face, z_threshold, &weight);
-  EXPECT_EQ(LayerSorter::B_BEFORE_A, overlap_result);
-}
-
-TEST(LayerSorterTest, IntersectingLayerOverlap) {
-  LayerSorter::ABCompareResult overlap_result;
-  const float z_threshold = 0.1f;
-  float weight = 0.f;
-
-  gfx::Transform perspective_matrix;
-  perspective_matrix.ApplyPerspectiveDepth(1000.0);
-
-  // Intersecting layers. An explicit order will be returned based on relative z
-  // values at the overlapping features but the weight returned should be zero.
-  gfx::Transform front_face_matrix;
-  front_face_matrix.Translate3d(0.0, 0.0, -4.0);
-  front_face_matrix.Translate(-1.0, -1.0);
-  LayerShape front_face(2.f, 2.f, perspective_matrix * front_face_matrix);
-
-  gfx::Transform through_matrix;
-  through_matrix.Translate3d(0.0, 0.0, -4.0);
-  through_matrix.RotateAboutYAxis(45.0);
-  through_matrix.Translate(-1.0, -1.0);
-  LayerShape rotated_face(2.f, 2.f, perspective_matrix * through_matrix);
-  overlap_result = LayerSorter::CheckOverlap(&front_face,
-                                             &rotated_face,
-                                             z_threshold,
-                                             &weight);
-  EXPECT_NE(LayerSorter::NONE, overlap_result);
-  EXPECT_EQ(0.f, weight);
-}
-
-TEST(LayerSorterTest, LayersAtAngleOverlap) {
-  LayerSorter::ABCompareResult overlap_result;
-  const float z_threshold = 0.1f;
-  float weight = 0.f;
-
-  // Trickier test with layers at an angle.
-  //
-  //   -x . . . . 0 . . . . +x
-  // -z             /
-  //  :            /----B----
-  //  0           C
-  //  : ----A----/
-  // +z         /
-  //
-  // C is in front of A and behind B (not what you'd expect by comparing
-  // centers). A and B don't overlap, so they're incomparable.
-
-  gfx::Transform transform_a;
-  transform_a.Translate3d(-6.0, 0.0, 1.0);
-  transform_a.Translate(-4.0, -10.0);
-  LayerShape layer_a(8.f, 20.f, transform_a);
-
-  gfx::Transform transform_b;
-  transform_b.Translate3d(6.0, 0.0, -1.0);
-  transform_b.Translate(-4.0, -10.0);
-  LayerShape layer_b(8.f, 20.f, transform_b);
-
-  gfx::Transform transform_c;
-  transform_c.RotateAboutYAxis(40.0);
-  transform_c.Translate(-4.0, -10.0);
-  LayerShape layer_c(8.f, 20.f, transform_c);
-
-  overlap_result =
-      LayerSorter::CheckOverlap(&layer_a, &layer_c, z_threshold, &weight);
-  EXPECT_EQ(LayerSorter::A_BEFORE_B, overlap_result);
-  overlap_result =
-      LayerSorter::CheckOverlap(&layer_c, &layer_b, z_threshold, &weight);
-  EXPECT_EQ(LayerSorter::A_BEFORE_B, overlap_result);
-  overlap_result =
-      LayerSorter::CheckOverlap(&layer_a, &layer_b, z_threshold, &weight);
-  EXPECT_EQ(LayerSorter::NONE, overlap_result);
-}
-
-TEST(LayerSorterTest, LayersUnderPathologicalPerspectiveTransform) {
-  LayerSorter::ABCompareResult overlap_result;
-  const float z_threshold = 0.1f;
-  float weight = 0.f;
-
-  // On perspective projection, if w becomes negative, the re-projected point
-  // will be invalid and un-usable. Correct code needs to clip away portions of
-  // the geometry where w < 0. If the code uses the invalid value, it will think
-  // that a layer has different bounds than it really does, which can cause
-  // things to sort incorrectly.
-
-  gfx::Transform perspective_matrix;
-  perspective_matrix.ApplyPerspectiveDepth(1);
-
-  gfx::Transform transform_a;
-  transform_a.Translate3d(-15.0, 0.0, -2.0);
-  transform_a.Translate(-5.0, -5.0);
-  LayerShape layer_a(10.f, 10.f, perspective_matrix * transform_a);
-
-  // With this sequence of transforms, when layer B is correctly clipped, it
-  // will be visible on the left half of the projection plane, in front of
-  // layer_a. When it is not clipped, its bounds will actually incorrectly
-  // appear much smaller and the correct sorting dependency will not be found.
-  gfx::Transform transform_b;
-  transform_b.Translate3d(0.f, 0.f, 0.7f);
-  transform_b.RotateAboutYAxis(45.0);
-  transform_b.Translate(-5.0, -5.0);
-  LayerShape layer_b(10.f, 10.f, perspective_matrix * transform_b);
-
-  // Sanity check that the test case actually covers the intended scenario,
-  // where part of layer B go behind the w = 0 plane.
-  gfx::QuadF test_quad = gfx::QuadF(gfx::RectF(-0.5f, -0.5f, 1.f, 1.f));
-  bool clipped = false;
-  MathUtil::MapQuad(perspective_matrix * transform_b, test_quad, &clipped);
-  ASSERT_TRUE(clipped);
-
-  overlap_result =
-      LayerSorter::CheckOverlap(&layer_a, &layer_b, z_threshold, &weight);
-  EXPECT_EQ(LayerSorter::A_BEFORE_B, overlap_result);
-}
-
-TEST(LayerSorterTest, VerifyExistingOrderingPreservedWhenNoZDiff) {
-  // If there is no reason to re-sort the layers (i.e. no 3d z difference), then
-  // the existing ordering provided on input should be retained. This test
-  // covers the fix in https://bugs.webkit.org/show_bug.cgi?id=75046. Before
-  // this fix, ordering was accidentally reversed, causing bugs in z-index
-  // ordering on websites when preserves3D triggered the LayerSorter.
-
-  // Input list of layers: [1, 2, 3, 4, 5].
-  // Expected output: [3, 4, 1, 2, 5].
-  //    - 1, 2, and 5 do not have a 3d z difference, and therefore their
-  //      relative ordering should be retained.
-  //    - 3 and 4 do not have a 3d z difference, and therefore their relative
-  //      ordering should be retained.
-  //    - 3 and 4 should be re-sorted so they are in front of 1, 2, and 5.
-
-  FakeImplProxy proxy;
-  TestSharedBitmapManager shared_bitmap_manager;
-  FakeLayerTreeHostImpl host_impl(&proxy, &shared_bitmap_manager);
-
-  scoped_ptr<LayerImpl> layer1 = LayerImpl::Create(host_impl.active_tree(), 1);
-  scoped_ptr<LayerImpl> layer2 = LayerImpl::Create(host_impl.active_tree(), 2);
-  scoped_ptr<LayerImpl> layer3 = LayerImpl::Create(host_impl.active_tree(), 3);
-  scoped_ptr<LayerImpl> layer4 = LayerImpl::Create(host_impl.active_tree(), 4);
-  scoped_ptr<LayerImpl> layer5 = LayerImpl::Create(host_impl.active_tree(), 5);
-
-  gfx::Transform BehindMatrix;
-  BehindMatrix.Translate3d(0.0, 0.0, 2.0);
-  gfx::Transform FrontMatrix;
-  FrontMatrix.Translate3d(0.0, 0.0, 1.0);
-
-  layer1->SetBounds(gfx::Size(10, 10));
-  layer1->SetContentBounds(gfx::Size(10, 10));
-  layer1->draw_properties().target_space_transform = BehindMatrix;
-  layer1->SetDrawsContent(true);
-
-  layer2->SetBounds(gfx::Size(20, 20));
-  layer2->SetContentBounds(gfx::Size(20, 20));
-  layer2->draw_properties().target_space_transform = BehindMatrix;
-  layer2->SetDrawsContent(true);
-
-  layer3->SetBounds(gfx::Size(30, 30));
-  layer3->SetContentBounds(gfx::Size(30, 30));
-  layer3->draw_properties().target_space_transform = FrontMatrix;
-  layer3->SetDrawsContent(true);
-
-  layer4->SetBounds(gfx::Size(40, 40));
-  layer4->SetContentBounds(gfx::Size(40, 40));
-  layer4->draw_properties().target_space_transform = FrontMatrix;
-  layer4->SetDrawsContent(true);
-
-  layer5->SetBounds(gfx::Size(50, 50));
-  layer5->SetContentBounds(gfx::Size(50, 50));
-  layer5->draw_properties().target_space_transform = BehindMatrix;
-  layer5->SetDrawsContent(true);
-
-  LayerImplList layer_list;
-  layer_list.push_back(layer1.get());
-  layer_list.push_back(layer2.get());
-  layer_list.push_back(layer3.get());
-  layer_list.push_back(layer4.get());
-  layer_list.push_back(layer5.get());
-
-  ASSERT_EQ(5u, layer_list.size());
-  EXPECT_EQ(1, layer_list[0]->id());
-  EXPECT_EQ(2, layer_list[1]->id());
-  EXPECT_EQ(3, layer_list[2]->id());
-  EXPECT_EQ(4, layer_list[3]->id());
-  EXPECT_EQ(5, layer_list[4]->id());
-
-  LayerSorter layer_sorter;
-  layer_sorter.Sort(layer_list.begin(), layer_list.end());
-
-  ASSERT_EQ(5u, layer_list.size());
-  EXPECT_EQ(3, layer_list[0]->id());
-  EXPECT_EQ(4, layer_list[1]->id());
-  EXPECT_EQ(1, layer_list[2]->id());
-  EXPECT_EQ(2, layer_list[3]->id());
-  EXPECT_EQ(5, layer_list[4]->id());
-}
-
-TEST(LayerSorterTest, VerifyConcidentLayerPrecisionLossResultsInDocumentOrder) {
-  FakeImplProxy proxy;
-  TestSharedBitmapManager shared_bitmap_manager;
-  FakeLayerTreeHostImpl host_impl(&proxy, &shared_bitmap_manager);
-
-  scoped_ptr<LayerImpl> layer1 = LayerImpl::Create(host_impl.active_tree(), 1);
-  scoped_ptr<LayerImpl> layer2 = LayerImpl::Create(host_impl.active_tree(), 2);
-
-  // Layer 1 should occur before layer 2 in paint.  However, due to numeric
-  // issues in the sorter, it will put the layers in the wrong order
-  // in some situations.  Here we test a patch that results in  document
-  // order rather than calculated order when numeric percision is suspect
-  // in calculated order.
-
-  gfx::Transform BehindMatrix;
-  BehindMatrix.Translate3d(0.f, 0.f, 0.999999f);
-  BehindMatrix.RotateAboutXAxis(38.5);
-  BehindMatrix.RotateAboutYAxis(77.0);
-  gfx::Transform FrontMatrix;
-  FrontMatrix.Translate3d(0, 0, 1.0);
-  FrontMatrix.RotateAboutXAxis(38.5);
-  FrontMatrix.RotateAboutYAxis(77.0);
-
-  layer1->SetBounds(gfx::Size(10, 10));
-  layer1->SetContentBounds(gfx::Size(10, 10));
-  layer1->draw_properties().target_space_transform = BehindMatrix;
-  layer1->SetDrawsContent(true);
-
-  layer2->SetBounds(gfx::Size(10, 10));
-  layer2->SetContentBounds(gfx::Size(10, 10));
-  layer2->draw_properties().target_space_transform = FrontMatrix;
-  layer2->SetDrawsContent(true);
-
-  LayerImplList layer_list;
-  layer_list.push_back(layer1.get());
-  layer_list.push_back(layer2.get());
-
-  ASSERT_EQ(2u, layer_list.size());
-  EXPECT_EQ(1, layer_list[0]->id());
-  EXPECT_EQ(2, layer_list[1]->id());
-
-  LayerSorter layer_sorter;
-  layer_sorter.Sort(layer_list.begin(), layer_list.end());
-
-  ASSERT_EQ(2u, layer_list.size());
-  EXPECT_EQ(1, layer_list[0]->id());
-  EXPECT_EQ(2, layer_list[1]->id());
-}
-
-}  // namespace
-}  // namespace cc
-
diff --git a/cc/trees/layer_tree_host_common.cc b/cc/trees/layer_tree_host_common.cc
index 9059ae1..064e91e 100644
--- a/cc/trees/layer_tree_host_common.cc
+++ b/cc/trees/layer_tree_host_common.cc
@@ -15,7 +15,6 @@
 #include "cc/layers/render_surface.h"
 #include "cc/layers/render_surface_impl.h"
 #include "cc/trees/draw_property_utils.h"
-#include "cc/trees/layer_sorter.h"
 #include "cc/trees/layer_tree_host.h"
 #include "cc/trees/layer_tree_impl.h"
 #include "ui/gfx/geometry/rect_conversions.h"
@@ -31,20 +30,6 @@
 
 ScrollAndScaleSet::~ScrollAndScaleSet() {}
 
-static void SortLayers(LayerList::iterator first,
-                       LayerList::iterator end,
-                       void* layer_sorter) {
-  NOTREACHED();
-}
-
-static void SortLayers(LayerImplList::iterator first,
-                       LayerImplList::iterator end,
-                       LayerSorter* layer_sorter) {
-  DCHECK(layer_sorter);
-  TRACE_EVENT0("cc", "LayerTreeHostCommon::SortLayers");
-  layer_sorter->Sort(first, end);
-}
-
 template <typename LayerType>
 static gfx::Vector2dF GetEffectiveScrollDelta(LayerType* layer) {
   // Layer's scroll offset can have an integer part and fractional part.
@@ -1281,7 +1266,6 @@
 
 template <typename LayerType>
 struct SubtreeGlobals {
-  LayerSorter* layer_sorter;
   int max_texture_size;
   float device_scale_factor;
   float page_scale_factor;
@@ -2371,17 +2355,6 @@
     return;
   }
 
-  // If preserves-3d then sort all the descendants in 3D so that they can be
-  // drawn from back to front. If the preserves-3d property is also set on the
-  // parent then skip the sorting as the parent will sort all the descendants
-  // anyway.
-  if (globals.layer_sorter && descendants.size() && layer->Is3dSorted() &&
-      !LayerIsInExisting3DRenderingContext(layer)) {
-    SortLayers(descendants.begin() + sorting_start_index,
-               descendants.end(),
-               globals.layer_sorter);
-  }
-
   UpdateAccumulatedSurfaceState<LayerType>(
       layer, local_drawable_content_rect_of_subtree, accumulated_surface_state);
 
@@ -2419,7 +2392,6 @@
   scaled_device_transform.Scale(inputs.device_scale_factor,
                                 inputs.device_scale_factor);
 
-  globals->layer_sorter = NULL;
   globals->max_texture_size = inputs.max_texture_size;
   globals->device_scale_factor =
       inputs.device_scale_factor * device_transform_scale;
@@ -2583,9 +2555,6 @@
   DataForRecursion<LayerImpl> data_for_recursion;
   ProcessCalcDrawPropsInputs(*inputs, &globals, &data_for_recursion);
 
-  LayerSorter layer_sorter;
-  globals.layer_sorter = &layer_sorter;
-
   PreCalculateMetaInformationRecursiveData recursive_data;
   PreCalculateMetaInformation(inputs->root_layer, &recursive_data);
   std::vector<AccumulatedSurfaceState<LayerImpl>> accumulated_surface_state;
diff --git a/cc/trees/layer_tree_host_common_perftest.cc b/cc/trees/layer_tree_host_common_perftest.cc
index 73d5c1d..1732e5588 100644
--- a/cc/trees/layer_tree_host_common_perftest.cc
+++ b/cc/trees/layer_tree_host_common_perftest.cc
@@ -25,7 +25,6 @@
 #include "cc/test/layer_tree_json_parser.h"
 #include "cc/test/layer_tree_test.h"
 #include "cc/test/paths.h"
-#include "cc/trees/layer_sorter.h"
 #include "cc/trees/layer_tree_impl.h"
 #include "testing/perf/perf_test.h"
 
@@ -162,55 +161,7 @@
   }
 };
 
-class LayerSorterMainTest : public CalcDrawPropsImplTest {
- public:
-  void RunSortLayers() { RunTest(false, false, false); }
-
-  void BeginTest() override { PostSetNeedsCommitToMainThread(); }
-
-  void DrawLayersOnThread(LayerTreeHostImpl* host_impl) override {
-    LayerTreeImpl* active_tree = host_impl->active_tree();
-    // First build the tree and then we'll start running tests on layersorter
-    // itself
-    bool can_render_to_separate_surface = true;
-    int max_texture_size = 8096;
-    DoCalcDrawPropertiesImpl(can_render_to_separate_surface,
-                             max_texture_size,
-                             active_tree,
-                             host_impl);
-
-    // Behaviour of this test is different from that of sorting in practice.
-    // In this case, all layers that exist in any 3D context are put into a list
-    // and are sorted as one big 3D context instead of several smaller ones.
-    BuildLayerImplList(active_tree->root_layer(), &base_list_);
-    timer_.Reset();
-    do {
-      // Here we'll move the layers into a LayerImpl list of their own to be
-      // sorted so we don't have a sorted list for every run after the first
-      LayerImplList test_list = base_list_;
-      layer_sorter_.Sort(test_list.begin(), test_list.end());
-      timer_.NextLap();
-    } while (!timer_.HasTimeLimitExpired());
-
-    EndTest();
-  }
-
-  void BuildLayerImplList(LayerImpl* layer, LayerImplList* list) {
-    if (layer->Is3dSorted()) {
-      list->push_back(layer);
-    }
-
-    for (size_t i = 0; i < layer->children().size(); i++) {
-      BuildLayerImplList(layer->children()[i], list);
-    }
-  }
-
- private:
-  LayerImplList base_list_;
-  LayerSorter layer_sorter_;
-};
-
-class BspTreePerfTest : public LayerSorterMainTest {
+class BspTreePerfTest : public CalcDrawPropsImplTest {
  public:
   void RunSortLayers() { RunTest(false, false, false); }
 
@@ -261,7 +212,18 @@
     EndTest();
   }
 
+  void BuildLayerImplList(LayerImpl* layer, LayerImplList* list) {
+    if (layer->Is3dSorted()) {
+      list->push_back(layer);
+    }
+
+    for (size_t i = 0; i < layer->children().size(); i++) {
+      BuildLayerImplList(layer->children()[i], list);
+    }
+  }
+
  private:
+  LayerImplList base_list_;
   int num_duplicates_;
 };
 
@@ -313,13 +275,13 @@
   RunCalcDrawProps();
 }
 
-TEST_F(LayerSorterMainTest, LayerSorterCubes) {
+TEST_F(BspTreePerfTest, LayerSorterCubes) {
   SetTestName("layer_sort_cubes");
   ReadTestFile("layer_sort_cubes");
   RunSortLayers();
 }
 
-TEST_F(LayerSorterMainTest, LayerSorterRubik) {
+TEST_F(BspTreePerfTest, LayerSorterRubik) {
   SetTestName("layer_sort_rubik");
   ReadTestFile("layer_sort_rubik");
   // TODO(vollick): Remove verify_property_trees setting after
diff --git a/cc/trees/layer_tree_host_common_unittest.cc b/cc/trees/layer_tree_host_common_unittest.cc
index 6c0d1ae7..d2c473e 100644
--- a/cc/trees/layer_tree_host_common_unittest.cc
+++ b/cc/trees/layer_tree_host_common_unittest.cc
@@ -3043,78 +3043,6 @@
   EXPECT_FALSE(child->draw_properties().sorted_for_recursion);
 }
 
-TEST_F(LayerTreeHostCommonTest, WillSortAtContextBoundary) {
-  // Creates a layer tree that looks as follows:
-  // * root (sorting-context-id1)
-  //   * parent (sorting-context-id2)
-  //     * child1 (sorting-context-id2)
-  //     * child2 (sorting-context-id2)
-  //
-  // This test ensures that we sort at |parent| even though both it and root are
-  // set to be 3d sorted.
-  FakeImplProxy proxy;
-  TestSharedBitmapManager shared_bitmap_manager;
-  FakeLayerTreeHostImpl host_impl(&proxy, &shared_bitmap_manager);
-
-  scoped_ptr<LayerImpl> root_ptr(LayerImpl::Create(host_impl.active_tree(), 1));
-  LayerImpl* root = root_ptr.get();
-  scoped_ptr<LayerImpl> parent_ptr(
-      LayerImpl::Create(host_impl.active_tree(), 2));
-  LayerImpl* parent = parent_ptr.get();
-  scoped_ptr<LayerImpl> child1_ptr(
-      LayerImpl::Create(host_impl.active_tree(), 3));
-  LayerImpl* child1 = child1_ptr.get();
-  scoped_ptr<LayerImpl> child2_ptr(
-      LayerImpl::Create(host_impl.active_tree(), 4));
-  LayerImpl* child2 = child2_ptr.get();
-
-  gfx::Transform identity_matrix;
-  gfx::Transform below_matrix;
-  below_matrix.Translate3d(0.f, 0.f, -10.f);
-  gfx::Transform above_matrix;
-  above_matrix.Translate3d(0.f, 0.f, 10.f);
-
-  SetLayerPropertiesForTesting(root, identity_matrix, gfx::Point3F(),
-                               gfx::PointF(), gfx::Size(100, 100), true, true,
-                               true);
-  SetLayerPropertiesForTesting(parent, identity_matrix, gfx::Point3F(),
-                               gfx::PointF(), gfx::Size(50, 50), true, true,
-                               true);
-  SetLayerPropertiesForTesting(child1, above_matrix, gfx::Point3F(),
-                               gfx::PointF(), gfx::Size(50, 50), true, true,
-                               false);
-  SetLayerPropertiesForTesting(child2, below_matrix, gfx::Point3F(),
-                               gfx::PointF(), gfx::Size(50, 50), true, true,
-                               false);
-
-  root->Set3dSortingContextId(3);
-  root->SetDrawsContent(true);
-  parent->Set3dSortingContextId(7);
-  parent->SetDrawsContent(true);
-  child1->Set3dSortingContextId(7);
-  child1->SetDrawsContent(true);
-  child2->Set3dSortingContextId(7);
-  child2->SetDrawsContent(true);
-
-  parent->AddChild(child1_ptr.Pass());
-  parent->AddChild(child2_ptr.Pass());
-  root->AddChild(parent_ptr.Pass());
-
-  LayerImplList render_surface_layer_list;
-  LayerTreeHostCommon::CalcDrawPropsImplInputsForTesting inputs(
-      root_ptr.get(), root->bounds(), &render_surface_layer_list);
-  inputs.can_adjust_raster_scales = true;
-  LayerTreeHostCommon::CalculateDrawProperties(&inputs);
-
-  EXPECT_TRUE(root->render_surface());
-  EXPECT_EQ(2u, render_surface_layer_list.size());
-
-  EXPECT_EQ(3u, parent->render_surface()->layer_list().size());
-  EXPECT_EQ(child2->id(), parent->render_surface()->layer_list().at(0)->id());
-  EXPECT_EQ(parent->id(), parent->render_surface()->layer_list().at(1)->id());
-  EXPECT_EQ(child1->id(), parent->render_surface()->layer_list().at(2)->id());
-}
-
 TEST_F(LayerTreeHostCommonTest,
        SingularNonAnimatingTransformDoesNotPreventClearingDrawProperties) {
   scoped_refptr<Layer> root = Layer::Create();
@@ -7660,110 +7588,6 @@
   EXPECT_TRUE(render_surface_layer_list.at(2)->render_surface());
 }
 
-TEST_F(LayerTreeHostCommonTest, DoNotClobberSorting) {
-  // We rearrange layer list contributions if we have to visit children out of
-  // order, but it should be a 'stable' rearrangement. That is, the layer list
-  // additions for a single layer should not be reordered, though their position
-  // wrt to the contributions due to a sibling may vary.
-  //
-  // + root
-  //   + scroll_child
-  //     + top_content
-  //     + bottom_content
-  //   + scroll_parent_border
-  //     + scroll_parent_clip
-  //       + scroll_parent
-  //
-  FakeImplProxy proxy;
-  TestSharedBitmapManager shared_bitmap_manager;
-  FakeLayerTreeHostImpl host_impl(&proxy, &shared_bitmap_manager);
-  host_impl.CreatePendingTree();
-  scoped_ptr<LayerImpl> root = LayerImpl::Create(host_impl.active_tree(), 1);
-  scoped_ptr<LayerImpl> scroll_parent_border =
-      LayerImpl::Create(host_impl.active_tree(), 2);
-  scoped_ptr<LayerImpl> scroll_parent_clip =
-      LayerImpl::Create(host_impl.active_tree(), 3);
-  scoped_ptr<LayerImpl> scroll_parent =
-      LayerImpl::Create(host_impl.active_tree(), 4);
-  scoped_ptr<LayerImpl> scroll_child =
-      LayerImpl::Create(host_impl.active_tree(), 5);
-  scoped_ptr<LayerImpl> bottom_content =
-      LayerImpl::Create(host_impl.active_tree(), 6);
-  scoped_ptr<LayerImpl> top_content =
-      LayerImpl::Create(host_impl.active_tree(), 7);
-
-  scroll_parent_clip->SetMasksToBounds(true);
-
-  scroll_child->SetScrollParent(scroll_parent.get());
-  scoped_ptr<std::set<LayerImpl*>> scroll_children(new std::set<LayerImpl*>);
-  scroll_children->insert(scroll_child.get());
-  scroll_parent->SetScrollChildren(scroll_children.release());
-
-  scroll_child->SetDrawsContent(true);
-  scroll_parent->SetDrawsContent(true);
-  top_content->SetDrawsContent(true);
-  bottom_content->SetDrawsContent(true);
-
-  gfx::Transform identity_transform;
-  gfx::Transform top_transform;
-  top_transform.Translate3d(0.0, 0.0, 5.0);
-  gfx::Transform bottom_transform;
-  bottom_transform.Translate3d(0.0, 0.0, 3.0);
-
-  SetLayerPropertiesForTesting(root.get(), identity_transform, gfx::Point3F(),
-                               gfx::PointF(), gfx::Size(50, 50), true, false,
-                               true);
-  SetLayerPropertiesForTesting(scroll_parent_border.get(), identity_transform,
-                               gfx::Point3F(), gfx::PointF(), gfx::Size(40, 40),
-                               true, false, false);
-  SetLayerPropertiesForTesting(scroll_parent_clip.get(), identity_transform,
-                               gfx::Point3F(), gfx::PointF(), gfx::Size(30, 30),
-                               true, false, false);
-  SetLayerPropertiesForTesting(scroll_parent.get(), identity_transform,
-                               gfx::Point3F(), gfx::PointF(), gfx::Size(50, 50),
-                               true, false, false);
-  SetLayerPropertiesForTesting(scroll_child.get(), identity_transform,
-                               gfx::Point3F(), gfx::PointF(), gfx::Size(50, 50),
-                               true, false, false);
-  SetLayerPropertiesForTesting(top_content.get(), top_transform, gfx::Point3F(),
-                               gfx::PointF(), gfx::Size(50, 50), false, true,
-                               true);
-  SetLayerPropertiesForTesting(bottom_content.get(), bottom_transform,
-                               gfx::Point3F(), gfx::PointF(), gfx::Size(50, 50),
-                               false, true, true);
-
-  scroll_child->SetShouldFlattenTransform(false);
-  scroll_child->Set3dSortingContextId(1);
-
-  scroll_child->AddChild(top_content.Pass());
-  scroll_child->AddChild(bottom_content.Pass());
-  root->AddChild(scroll_child.Pass());
-
-  scroll_parent_clip->AddChild(scroll_parent.Pass());
-  scroll_parent_border->AddChild(scroll_parent_clip.Pass());
-  root->AddChild(scroll_parent_border.Pass());
-
-  LayerImplList render_surface_layer_list;
-  LayerTreeHostCommon::CalcDrawPropsImplInputsForTesting inputs(
-      root.get(), root->bounds(), &render_surface_layer_list);
-
-  LayerTreeHostCommon::CalculateDrawProperties(&inputs);
-
-  EXPECT_TRUE(root->render_surface());
-
-  // If we don't sort by depth and let the layers get added in the order they
-  // would normally be visited in, then layers 6 and 7 will be out of order. In
-  // other words, although we've had to shift 5, 6, and 7 to appear before 4
-  // in the list (because of the scroll parent relationship), this should not
-  // have an effect on the the order of 5, 6, and 7 (which had been reordered
-  // due to layer sorting).
-  EXPECT_EQ(4u, root->render_surface()->layer_list().size());
-  EXPECT_EQ(5, root->render_surface()->layer_list().at(0)->id());
-  EXPECT_EQ(6, root->render_surface()->layer_list().at(1)->id());
-  EXPECT_EQ(7, root->render_surface()->layer_list().at(2)->id());
-  EXPECT_EQ(4, root->render_surface()->layer_list().at(3)->id());
-}
-
 TEST_F(LayerTreeHostCommonTest, ScrollCompensationWithRounding) {
   // This test verifies that a scrolling layer that gets snapped to
   // integer coordinates doesn't move a fixed position child.
diff --git a/cc/trees/layer_tree_host_pixeltest_filters.cc b/cc/trees/layer_tree_host_pixeltest_filters.cc
index 76acaa8..dd3cf73 100644
--- a/cc/trees/layer_tree_host_pixeltest_filters.cc
+++ b/cc/trees/layer_tree_host_pixeltest_filters.cc
@@ -97,8 +97,8 @@
 }
 
 TEST_F(LayerTreeHostFiltersPixelTest, BackgroundFilterBlurOffAxis) {
-  scoped_refptr<SolidColorLayer> background = CreateSolidColorLayer(
-      gfx::Rect(200, 200), SK_ColorWHITE);
+  scoped_refptr<SolidColorLayer> background =
+      CreateSolidColorLayer(gfx::Rect(200, 200), SK_ColorTRANSPARENT);
 
   // This verifies that the perspective of the clear layer (with black border)
   // does not influence the blending of the green box behind it. Also verifies
diff --git a/cc/trees/layer_tree_impl_unittest.cc b/cc/trees/layer_tree_impl_unittest.cc
index 67f98e65..06df3c2 100644
--- a/cc/trees/layer_tree_impl_unittest.cc
+++ b/cc/trees/layer_tree_impl_unittest.cc
@@ -920,14 +920,6 @@
   ASSERT_TRUE(grand_child1);
   ASSERT_EQ(1u, RenderSurfaceLayerList().size());
 
-  RenderSurfaceImpl* root_render_surface =
-      host_impl().active_tree()->root_layer()->render_surface();
-  ASSERT_EQ(4u, root_render_surface->layer_list().size());
-  ASSERT_EQ(3, root_render_surface->layer_list().at(0)->id());
-  ASSERT_EQ(1, root_render_surface->layer_list().at(1)->id());
-  ASSERT_EQ(2, root_render_surface->layer_list().at(2)->id());
-  ASSERT_EQ(4, root_render_surface->layer_list().at(3)->id());
-
   // Nothing overlaps the root_layer at (1, 1), so hit testing there should find
   // the root layer.
   gfx::Point test_point = gfx::Point(1, 1);
diff --git a/cc/trees/occlusion_tracker.cc b/cc/trees/occlusion_tracker.cc
index 388eb6ee2..0f0ce158 100644
--- a/cc/trees/occlusion_tracker.cc
+++ b/cc/trees/occlusion_tracker.cc
@@ -155,7 +155,7 @@
   return layer->Is3dSorted();
 }
 static inline bool LayerIsInUnsorted3dRenderingContext(const LayerImpl* layer) {
-  return false;
+  return layer->Is3dSorted();
 }
 
 template <typename LayerType>
diff --git a/cc/trees/occlusion_tracker_unittest.cc b/cc/trees/occlusion_tracker_unittest.cc
index e3639ad..f3f2185 100644
--- a/cc/trees/occlusion_tracker_unittest.cc
+++ b/cc/trees/occlusion_tracker_unittest.cc
@@ -1540,56 +1540,6 @@
   }
 };
 
-// This test requires accumulating occlusion of 3d layers, which are skipped by
-// the occlusion tracker on the main thread. So this test should run on the impl
-// thread.
-IMPL_THREAD_TEST(OcclusionTrackerTestLayerBehindCameraDoesNotOcclude);
-
-template <class Types>
-class OcclusionTrackerTestLargePixelsOccludeInsideClipRect
-    : public OcclusionTrackerTest<Types> {
- protected:
-  explicit OcclusionTrackerTestLargePixelsOccludeInsideClipRect(
-      bool opaque_layers)
-      : OcclusionTrackerTest<Types>(opaque_layers) {}
-  void RunMyTest() override {
-    gfx::Transform transform;
-    transform.Translate(50.0, 50.0);
-    transform.ApplyPerspectiveDepth(100.0);
-    transform.Translate3d(0.0, 0.0, 99.0);
-    transform.Translate(-50.0, -50.0);
-
-    typename Types::ContentLayerType* parent = this->CreateRoot(
-        this->identity_matrix, gfx::PointF(), gfx::Size(100, 100));
-    parent->SetMasksToBounds(true);
-    typename Types::ContentLayerType* layer = this->CreateDrawingLayer(
-        parent, transform, gfx::PointF(), gfx::Size(100, 100), true);
-    parent->SetShouldFlattenTransform(false);
-    parent->Set3dSortingContextId(1);
-    layer->SetShouldFlattenTransform(false);
-    layer->Set3dSortingContextId(1);
-    this->CalcDrawEtc(parent);
-
-    TestOcclusionTrackerWithClip<typename Types::LayerType> occlusion(
-        gfx::Rect(0, 0, 1000, 1000));
-
-    // This is very close to the camera, so pixels in its visible_content_rect()
-    // will actually go outside of the layer's clip rect.  Ensure that those
-    // pixels don't occlude things outside the clip rect.
-    this->VisitLayer(layer, &occlusion);
-    this->EnterLayer(parent, &occlusion);
-    EXPECT_EQ(gfx::Rect(0, 0, 100, 100).ToString(),
-              occlusion.occlusion_from_inside_target().ToString());
-    EXPECT_EQ(gfx::Rect().ToString(),
-              occlusion.occlusion_from_outside_target().ToString());
-  }
-};
-
-// This test requires accumulating occlusion of 3d layers, which are skipped by
-// the occlusion tracker on the main thread. So this test should run on the impl
-// thread.
-IMPL_THREAD_TEST(OcclusionTrackerTestLargePixelsOccludeInsideClipRect);
-
 template <class Types>
 class OcclusionTrackerTestAnimationOpacity1OnMainThread
     : public OcclusionTrackerTest<Types> {