Upstream version 10.39.225.0
[platform/framework/web/crosswalk.git] / src / cc / layers / picture_layer_impl.cc
index 5bef194..37c8328 100644 (file)
@@ -6,7 +6,9 @@
 
 #include <algorithm>
 #include <limits>
+#include <set>
 
+#include "base/debug/trace_event_argument.h"
 #include "base/time/time.h"
 #include "cc/base/math_util.h"
 #include "cc/base/util.h"
 #include "cc/debug/micro_benchmark_impl.h"
 #include "cc/debug/traced_value.h"
 #include "cc/layers/append_quads_data.h"
-#include "cc/layers/quad_sink.h"
+#include "cc/layers/solid_color_layer_impl.h"
+#include "cc/output/begin_frame_args.h"
 #include "cc/quads/checkerboard_draw_quad.h"
 #include "cc/quads/debug_border_draw_quad.h"
 #include "cc/quads/picture_draw_quad.h"
 #include "cc/quads/solid_color_draw_quad.h"
 #include "cc/quads/tile_draw_quad.h"
 #include "cc/resources/tile_manager.h"
+#include "cc/trees/layer_tree_impl.h"
+#include "cc/trees/occlusion_tracker.h"
 #include "ui/gfx/quad_f.h"
 #include "ui/gfx/rect_conversions.h"
 #include "ui/gfx/size_conversions.h"
@@ -30,7 +35,7 @@ const float kMaxScaleRatioDuringPinch = 2.0f;
 
 // When creating a new tiling during pinch, snap to an existing
 // tiling's scale if the desired scale is within this ratio.
-const float kSnapToExistingTilingRatio = 0.2f;
+const float kSnapToExistingTilingRatio = 1.2f;
 
 // Estimate skewport 60 frames ahead for pre-rasterization on the CPU.
 const float kCpuSkewportTargetTimeInFrames = 60.0f;
@@ -38,15 +43,26 @@ const float kCpuSkewportTargetTimeInFrames = 60.0f;
 // Don't pre-rasterize on the GPU (except for kBackflingGuardDistancePixels in
 // TileManager::BinFromTilePriority).
 const float kGpuSkewportTargetTimeInFrames = 0.0f;
+
 }  // namespace
 
 namespace cc {
 
+PictureLayerImpl::Pair::Pair() : active(NULL), pending(NULL) {
+}
+
+PictureLayerImpl::Pair::Pair(PictureLayerImpl* active_layer,
+                             PictureLayerImpl* pending_layer)
+    : active(active_layer), pending(pending_layer) {
+}
+
+PictureLayerImpl::Pair::~Pair() {
+}
+
 PictureLayerImpl::PictureLayerImpl(LayerTreeImpl* tree_impl, int id)
     : LayerImpl(tree_impl, id),
       twin_layer_(NULL),
       pile_(PicturePileImpl::Create()),
-      is_mask_(false),
       ideal_page_scale_(0.f),
       ideal_device_scale_(0.f),
       ideal_source_scale_(0.f),
@@ -57,17 +73,14 @@ PictureLayerImpl::PictureLayerImpl(LayerTreeImpl* tree_impl, int id)
       raster_contents_scale_(0.f),
       low_res_raster_contents_scale_(0.f),
       raster_source_scale_is_fixed_(false),
-      was_animating_transform_to_screen_(false),
-      is_using_lcd_text_(tree_impl->settings().can_use_lcd_text),
+      was_screen_space_transform_animating_(false),
       needs_post_commit_initialization_(true),
-      should_update_tile_priorities_(false),
-      should_use_low_res_tiling_(tree_impl->settings().create_low_res_tiling),
-      layer_needs_to_register_itself_(true) {
+      should_update_tile_priorities_(false) {
+  layer_tree_impl()->RegisterPictureLayerImpl(this);
 }
 
 PictureLayerImpl::~PictureLayerImpl() {
-  if (!layer_needs_to_register_itself_)
-    layer_tree_impl()->tile_manager()->UnregisterPictureLayerImpl(this);
+  layer_tree_impl()->UnregisterPictureLayerImpl(this);
 }
 
 const char* PictureLayerImpl::LayerTypeAsString() const {
@@ -97,31 +110,32 @@ void PictureLayerImpl::PushPropertiesTo(LayerImpl* base_layer) {
   LayerImpl::PushPropertiesTo(base_layer);
 
   // When the pending tree pushes to the active tree, the pending twin
-  // disappears.
+  // becomes recycled.
   layer_impl->twin_layer_ = NULL;
   twin_layer_ = NULL;
 
-  layer_impl->SetIsMask(is_mask_);
   layer_impl->pile_ = pile_;
 
+  DCHECK(!pile_->is_solid_color() || !tilings_->num_tilings());
   // Tilings would be expensive to push, so we swap.
   layer_impl->tilings_.swap(tilings_);
-
-  // Ensure that we don't have any tiles that are out of date.
-  if (tilings_)
-    tilings_->RemoveTilesInRegion(invalidation_);
-
   layer_impl->tilings_->SetClient(layer_impl);
   if (tilings_)
     tilings_->SetClient(this);
 
+  // Ensure that the recycle tree doesn't have any unshared tiles.
+  if (tilings_ && pile_->is_solid_color())
+    tilings_->RemoveAllTilings();
+
+  // Remove invalidated tiles from what will become a recycle tree.
+  if (tilings_)
+    tilings_->RemoveTilesInRegion(invalidation_);
+
   layer_impl->raster_page_scale_ = raster_page_scale_;
   layer_impl->raster_device_scale_ = raster_device_scale_;
   layer_impl->raster_source_scale_ = raster_source_scale_;
   layer_impl->raster_contents_scale_ = raster_contents_scale_;
   layer_impl->low_res_raster_contents_scale_ = low_res_raster_contents_scale_;
-
-  layer_impl->UpdateLCDTextStatus(is_using_lcd_text_);
   layer_impl->needs_post_commit_initialization_ = false;
 
   // The invalidation on this soon-to-be-recycled layer must be cleared to
@@ -136,36 +150,76 @@ void PictureLayerImpl::PushPropertiesTo(LayerImpl* base_layer) {
   needs_push_properties_ = true;
 }
 
-void PictureLayerImpl::AppendQuads(QuadSink* quad_sink,
-                                   AppendQuadsData* append_quads_data) {
+void PictureLayerImpl::AppendQuads(
+    RenderPass* render_pass,
+    const OcclusionTracker<LayerImpl>& occlusion_tracker,
+    AppendQuadsData* append_quads_data) {
   DCHECK(!needs_post_commit_initialization_);
-  gfx::Rect rect(visible_content_rect());
-  gfx::Rect content_rect(content_bounds());
 
-  SharedQuadState* shared_quad_state = quad_sink->CreateSharedQuadState();
-  PopulateSharedQuadState(shared_quad_state);
+  SharedQuadState* shared_quad_state =
+      render_pass->CreateAndAppendSharedQuadState();
+
+  if (pile_->is_solid_color()) {
+    PopulateSharedQuadState(shared_quad_state);
+
+    AppendDebugBorderQuad(
+        render_pass, content_bounds(), shared_quad_state, append_quads_data);
+
+    SolidColorLayerImpl::AppendSolidQuads(
+        render_pass,
+        occlusion_tracker,
+        shared_quad_state,
+        content_bounds(),
+        draw_properties().target_space_transform,
+        pile_->solid_color());
+    return;
+  }
+
+  float max_contents_scale = MaximumTilingContentsScale();
+  gfx::Transform scaled_draw_transform = draw_transform();
+  scaled_draw_transform.Scale(SK_MScalar1 / max_contents_scale,
+                              SK_MScalar1 / max_contents_scale);
+  gfx::Size scaled_content_bounds =
+      gfx::ToCeiledSize(gfx::ScaleSize(content_bounds(), max_contents_scale));
+
+  gfx::Rect scaled_visible_content_rect =
+      gfx::ScaleToEnclosingRect(visible_content_rect(), max_contents_scale);
+  scaled_visible_content_rect.Intersect(gfx::Rect(scaled_content_bounds));
+
+  Occlusion occlusion =
+      occlusion_tracker.GetCurrentOcclusionForLayer(scaled_draw_transform);
+
+  shared_quad_state->SetAll(scaled_draw_transform,
+                            scaled_content_bounds,
+                            scaled_visible_content_rect,
+                            draw_properties().clip_rect,
+                            draw_properties().is_clipped,
+                            draw_properties().opacity,
+                            blend_mode(),
+                            sorting_context_id_);
 
   if (current_draw_mode_ == DRAW_MODE_RESOURCELESS_SOFTWARE) {
     AppendDebugBorderQuad(
-        quad_sink,
+        render_pass,
+        scaled_content_bounds,
         shared_quad_state,
         append_quads_data,
         DebugColors::DirectPictureBorderColor(),
         DebugColors::DirectPictureBorderWidth(layer_tree_impl()));
 
-    gfx::Rect geometry_rect = rect;
+    gfx::Rect geometry_rect = scaled_visible_content_rect;
     gfx::Rect opaque_rect = contents_opaque() ? geometry_rect : gfx::Rect();
     gfx::Rect visible_geometry_rect =
-        quad_sink->UnoccludedContentRect(geometry_rect, draw_transform());
+        occlusion.GetUnoccludedContentRect(geometry_rect);
     if (visible_geometry_rect.IsEmpty())
       return;
 
-    gfx::Size texture_size = rect.size();
+    gfx::Size texture_size = scaled_visible_content_rect.size();
     gfx::RectF texture_rect = gfx::RectF(texture_size);
-    gfx::Rect quad_content_rect = rect;
-    float contents_scale = contents_scale_x();
+    gfx::Rect quad_content_rect = scaled_visible_content_rect;
 
-    scoped_ptr<PictureDrawQuad> quad = PictureDrawQuad::Create();
+    PictureDrawQuad* quad =
+        render_pass->CreateAndAppendDrawQuad<PictureDrawQuad>();
     quad->SetNew(shared_quad_state,
                  geometry_rect,
                  opaque_rect,
@@ -174,18 +228,20 @@ void PictureLayerImpl::AppendQuads(QuadSink* quad_sink,
                  texture_size,
                  RGBA_8888,
                  quad_content_rect,
-                 contents_scale,
+                 max_contents_scale,
                  pile_);
-    quad_sink->Append(quad.PassAs<DrawQuad>());
-    append_quads_data->num_missing_tiles++;
     return;
   }
 
-  AppendDebugBorderQuad(quad_sink, shared_quad_state, append_quads_data);
+  AppendDebugBorderQuad(
+      render_pass, scaled_content_bounds, shared_quad_state, append_quads_data);
 
   if (ShowDebugBorders()) {
     for (PictureLayerTilingSet::CoverageIterator iter(
-        tilings_.get(), contents_scale_x(), rect, ideal_contents_scale_);
+             tilings_.get(),
+             max_contents_scale,
+             scaled_visible_content_rect,
+             ideal_contents_scale_);
          iter;
          ++iter) {
       SkColor color;
@@ -205,7 +261,7 @@ void PictureLayerImpl::AppendQuads(QuadSink* quad_sink,
         } else if (iter->priority(ACTIVE_TREE).resolution == LOW_RESOLUTION) {
           color = DebugColors::LowResTileBorderColor();
           width = DebugColors::LowResTileBorderWidth(layer_tree_impl());
-        } else if (iter->contents_scale() > contents_scale_x()) {
+        } else if (iter->contents_scale() > max_contents_scale) {
           color = DebugColors::ExtraHighResTileBorderColor();
           width = DebugColors::ExtraHighResTileBorderWidth(layer_tree_impl());
         } else {
@@ -217,8 +273,8 @@ void PictureLayerImpl::AppendQuads(QuadSink* quad_sink,
         width = DebugColors::MissingTileBorderWidth(layer_tree_impl());
       }
 
-      scoped_ptr<DebugBorderDrawQuad> debug_border_quad =
-          DebugBorderDrawQuad::Create();
+      DebugBorderDrawQuad* debug_border_quad =
+          render_pass->CreateAndAppendDrawQuad<DebugBorderDrawQuad>();
       gfx::Rect geometry_rect = iter.geometry_rect();
       gfx::Rect visible_geometry_rect = geometry_rect;
       debug_border_quad->SetNew(shared_quad_state,
@@ -226,7 +282,6 @@ void PictureLayerImpl::AppendQuads(QuadSink* quad_sink,
                                 visible_geometry_rect,
                                 color,
                                 width);
-      quad_sink->Append(debug_border_quad.PassAs<DrawQuad>());
     }
   }
 
@@ -234,106 +289,133 @@ void PictureLayerImpl::AppendQuads(QuadSink* quad_sink,
   // unused can be considered for removal.
   std::vector<PictureLayerTiling*> seen_tilings;
 
-  for (PictureLayerTilingSet::CoverageIterator iter(
-      tilings_.get(), contents_scale_x(), rect, ideal_contents_scale_);
+  // Ignore missing tiles outside of viewport for tile priority. This is
+  // normally the same as draw viewport but can be independently overridden by
+  // embedders like Android WebView with SetExternalDrawConstraints.
+  gfx::Rect scaled_viewport_for_tile_priority = gfx::ScaleToEnclosingRect(
+      GetViewportForTilePriorityInContentSpace(), max_contents_scale);
+
+  size_t missing_tile_count = 0u;
+  size_t on_demand_missing_tile_count = 0u;
+  for (PictureLayerTilingSet::CoverageIterator iter(tilings_.get(),
+                                                    max_contents_scale,
+                                                    scaled_visible_content_rect,
+                                                    ideal_contents_scale_);
        iter;
        ++iter) {
     gfx::Rect geometry_rect = iter.geometry_rect();
+    gfx::Rect opaque_rect = contents_opaque() ? geometry_rect : gfx::Rect();
     gfx::Rect visible_geometry_rect =
-        quad_sink->UnoccludedContentRect(geometry_rect, draw_transform());
+        occlusion.GetUnoccludedContentRect(geometry_rect);
     if (visible_geometry_rect.IsEmpty())
       continue;
 
     append_quads_data->visible_content_area +=
         visible_geometry_rect.width() * visible_geometry_rect.height();
 
-    if (!*iter || !iter->IsReadyToDraw()) {
+    bool has_draw_quad = false;
+    if (*iter && iter->IsReadyToDraw()) {
+      const ManagedTileState::TileVersion& tile_version =
+          iter->GetTileVersionForDrawing();
+      switch (tile_version.mode()) {
+        case ManagedTileState::TileVersion::RESOURCE_MODE: {
+          gfx::RectF texture_rect = iter.texture_rect();
+
+          // The raster_contents_scale_ is the best scale that the layer is
+          // trying to produce, even though it may not be ideal. Since that's
+          // the best the layer can promise in the future, consider those as
+          // complete. But if a tile is ideal scale, we don't want to consider
+          // it incomplete and trying to replace it with a tile at a worse
+          // scale.
+          if (iter->contents_scale() != raster_contents_scale_ &&
+              iter->contents_scale() != ideal_contents_scale_ &&
+              geometry_rect.Intersects(scaled_viewport_for_tile_priority)) {
+            append_quads_data->num_incomplete_tiles++;
+          }
+
+          TileDrawQuad* quad =
+              render_pass->CreateAndAppendDrawQuad<TileDrawQuad>();
+          quad->SetNew(shared_quad_state,
+                       geometry_rect,
+                       opaque_rect,
+                       visible_geometry_rect,
+                       tile_version.get_resource_id(),
+                       texture_rect,
+                       iter.texture_size(),
+                       tile_version.contents_swizzled());
+          has_draw_quad = true;
+          break;
+        }
+        case ManagedTileState::TileVersion::PICTURE_PILE_MODE: {
+          if (!layer_tree_impl()
+                   ->GetRendererCapabilities()
+                   .allow_rasterize_on_demand) {
+            ++on_demand_missing_tile_count;
+            break;
+          }
+
+          gfx::RectF texture_rect = iter.texture_rect();
+
+          ResourceProvider* resource_provider =
+              layer_tree_impl()->resource_provider();
+          ResourceFormat format =
+              resource_provider->memory_efficient_texture_format();
+          PictureDrawQuad* quad =
+              render_pass->CreateAndAppendDrawQuad<PictureDrawQuad>();
+          quad->SetNew(shared_quad_state,
+                       geometry_rect,
+                       opaque_rect,
+                       visible_geometry_rect,
+                       texture_rect,
+                       iter.texture_size(),
+                       format,
+                       iter->content_rect(),
+                       iter->contents_scale(),
+                       pile_);
+          has_draw_quad = true;
+          break;
+        }
+        case ManagedTileState::TileVersion::SOLID_COLOR_MODE: {
+          SolidColorDrawQuad* quad =
+              render_pass->CreateAndAppendDrawQuad<SolidColorDrawQuad>();
+          quad->SetNew(shared_quad_state,
+                       geometry_rect,
+                       visible_geometry_rect,
+                       tile_version.get_solid_color(),
+                       false);
+          has_draw_quad = true;
+          break;
+        }
+      }
+    }
+
+    if (!has_draw_quad) {
       if (draw_checkerboard_for_missing_tiles()) {
-        scoped_ptr<CheckerboardDrawQuad> quad = CheckerboardDrawQuad::Create();
+        CheckerboardDrawQuad* quad =
+            render_pass->CreateAndAppendDrawQuad<CheckerboardDrawQuad>();
         SkColor color = DebugColors::DefaultCheckerboardColor();
         quad->SetNew(
             shared_quad_state, geometry_rect, visible_geometry_rect, color);
-        quad_sink->Append(quad.PassAs<DrawQuad>());
       } else {
         SkColor color = SafeOpaqueBackgroundColor();
-        scoped_ptr<SolidColorDrawQuad> quad = SolidColorDrawQuad::Create();
+        SolidColorDrawQuad* quad =
+            render_pass->CreateAndAppendDrawQuad<SolidColorDrawQuad>();
         quad->SetNew(shared_quad_state,
                      geometry_rect,
                      visible_geometry_rect,
                      color,
                      false);
-        quad_sink->Append(quad.PassAs<DrawQuad>());
       }
 
-      append_quads_data->num_missing_tiles++;
-      append_quads_data->had_incomplete_tile = true;
+      if (geometry_rect.Intersects(scaled_viewport_for_tile_priority)) {
+        append_quads_data->num_missing_tiles++;
+        ++missing_tile_count;
+      }
       append_quads_data->approximated_visible_content_area +=
           visible_geometry_rect.width() * visible_geometry_rect.height();
       continue;
     }
 
-    const ManagedTileState::TileVersion& tile_version =
-        iter->GetTileVersionForDrawing();
-    scoped_ptr<DrawQuad> draw_quad;
-    switch (tile_version.mode()) {
-      case ManagedTileState::TileVersion::RESOURCE_MODE: {
-        gfx::RectF texture_rect = iter.texture_rect();
-        gfx::Rect opaque_rect = iter->opaque_rect();
-        opaque_rect.Intersect(geometry_rect);
-
-        if (iter->contents_scale() != ideal_contents_scale_)
-          append_quads_data->had_incomplete_tile = true;
-
-        scoped_ptr<TileDrawQuad> quad = TileDrawQuad::Create();
-        quad->SetNew(shared_quad_state,
-                     geometry_rect,
-                     opaque_rect,
-                     visible_geometry_rect,
-                     tile_version.get_resource_id(),
-                     texture_rect,
-                     iter.texture_size(),
-                     tile_version.contents_swizzled());
-        draw_quad = quad.PassAs<DrawQuad>();
-        break;
-      }
-      case ManagedTileState::TileVersion::PICTURE_PILE_MODE: {
-        gfx::RectF texture_rect = iter.texture_rect();
-        gfx::Rect opaque_rect = iter->opaque_rect();
-        opaque_rect.Intersect(geometry_rect);
-
-        ResourceProvider* resource_provider =
-            layer_tree_impl()->resource_provider();
-        ResourceFormat format =
-            resource_provider->memory_efficient_texture_format();
-        scoped_ptr<PictureDrawQuad> quad = PictureDrawQuad::Create();
-        quad->SetNew(shared_quad_state,
-                     geometry_rect,
-                     opaque_rect,
-                     visible_geometry_rect,
-                     texture_rect,
-                     iter.texture_size(),
-                     format,
-                     iter->content_rect(),
-                     iter->contents_scale(),
-                     pile_);
-        draw_quad = quad.PassAs<DrawQuad>();
-        break;
-      }
-      case ManagedTileState::TileVersion::SOLID_COLOR_MODE: {
-        scoped_ptr<SolidColorDrawQuad> quad = SolidColorDrawQuad::Create();
-        quad->SetNew(shared_quad_state,
-                     geometry_rect,
-                     visible_geometry_rect,
-                     tile_version.get_solid_color(),
-                     false);
-        draw_quad = quad.PassAs<DrawQuad>();
-        break;
-      }
-    }
-
-    DCHECK(draw_quad);
-    quad_sink->Append(draw_quad.Pass());
-
     if (iter->priority(ACTIVE_TREE).resolution != HIGH_RESOLUTION) {
       append_quads_data->approximated_visible_content_area +=
           visible_geometry_rect.width() * visible_geometry_rect.height();
@@ -343,6 +425,16 @@ void PictureLayerImpl::AppendQuads(QuadSink* quad_sink,
       seen_tilings.push_back(iter.CurrentTiling());
   }
 
+  if (missing_tile_count) {
+    TRACE_EVENT_INSTANT2("cc",
+                         "PictureLayerImpl::AppendQuads checkerboard",
+                         TRACE_EVENT_SCOPE_THREAD,
+                         "missing_tile_count",
+                         missing_tile_count,
+                         "on_demand_missing_tile_count",
+                         on_demand_missing_tile_count);
+  }
+
   // Aggressively remove any tilings that are not seen to save memory. Note
   // that this is at the expense of doing cause more frequent re-painting. A
   // better scheme would be to maintain a tighter visible_content_rect for the
@@ -350,30 +442,62 @@ void PictureLayerImpl::AppendQuads(QuadSink* quad_sink,
   CleanUpTilingsOnActiveLayer(seen_tilings);
 }
 
-void PictureLayerImpl::DidUnregisterLayer() {
-  layer_needs_to_register_itself_ = true;
-}
+void PictureLayerImpl::UpdateTiles(
+    const Occlusion& occlusion_in_content_space) {
+  TRACE_EVENT0("cc", "PictureLayerImpl::UpdateTiles");
+  DCHECK_EQ(1.f, contents_scale_x());
+  DCHECK_EQ(1.f, contents_scale_y());
 
-void PictureLayerImpl::UpdateTilePriorities() {
-  DCHECK(!needs_post_commit_initialization_);
-  CHECK(should_update_tile_priorities_);
+  DoPostCommitInitializationIfNeeded();
+
+  visible_rect_for_tile_priority_ = visible_content_rect();
+  viewport_rect_for_tile_priority_ =
+      layer_tree_impl()->ViewportRectForTilePriority();
+  screen_space_transform_for_tile_priority_ = screen_space_transform();
 
-  if (layer_needs_to_register_itself_) {
-    layer_tree_impl()->tile_manager()->RegisterPictureLayerImpl(this);
-    layer_needs_to_register_itself_ = false;
+  if (!CanHaveTilings()) {
+    ideal_page_scale_ = 0.f;
+    ideal_device_scale_ = 0.f;
+    ideal_contents_scale_ = 0.f;
+    ideal_source_scale_ = 0.f;
+    SanityCheckTilingState();
+    return;
   }
 
-  if (layer_tree_impl()->device_viewport_valid_for_tile_management()) {
-    visible_rect_for_tile_priority_ = visible_content_rect();
-    viewport_size_for_tile_priority_ = layer_tree_impl()->DrawViewportSize();
-    screen_space_transform_for_tile_priority_ = screen_space_transform();
+  UpdateIdealScales();
+
+  DCHECK(tilings_->num_tilings() > 0 || raster_contents_scale_ == 0.f)
+      << "A layer with no tilings shouldn't have valid raster scales";
+  if (!raster_contents_scale_ || ShouldAdjustRasterScale()) {
+    RecalculateRasterScales();
+    AddTilingsForRasterScale();
   }
 
-  if (!tilings_->num_tilings())
-    return;
+  DCHECK(raster_page_scale_);
+  DCHECK(raster_device_scale_);
+  DCHECK(raster_source_scale_);
+  DCHECK(raster_contents_scale_);
+  DCHECK(low_res_raster_contents_scale_);
+
+  was_screen_space_transform_animating_ =
+      draw_properties().screen_space_transform_is_animating;
+
+  should_update_tile_priorities_ = true;
+
+  UpdateTilePriorities(occlusion_in_content_space);
+
+  if (layer_tree_impl()->IsPendingTree())
+    MarkVisibleResourcesAsRequired();
+}
+
+void PictureLayerImpl::UpdateTilePriorities(
+    const Occlusion& occlusion_in_content_space) {
+  DCHECK(!pile_->is_solid_color() || !tilings_->num_tilings());
+
+  TRACE_EVENT0("cc", "PictureLayerImpl::UpdateTilePriorities");
 
   double current_frame_time_in_seconds =
-      (layer_tree_impl()->CurrentFrameTimeTicks() -
+      (layer_tree_impl()->CurrentBeginFrameArgs().frame_time -
        base::TimeTicks()).InSecondsF();
 
   bool tiling_needs_update = false;
@@ -387,38 +511,52 @@ void PictureLayerImpl::UpdateTilePriorities() {
   if (!tiling_needs_update)
     return;
 
-  UpdateLCDTextStatus(can_use_lcd_text());
+  gfx::Rect viewport_rect_in_layer_space =
+      GetViewportForTilePriorityInContentSpace();
+  WhichTree tree =
+      layer_tree_impl()->IsActiveTree() ? ACTIVE_TREE : PENDING_TREE;
+  for (size_t i = 0; i < tilings_->num_tilings(); ++i) {
+    // Pass |occlusion_in_content_space| for |occlusion_in_layer_space| since
+    // they are the same space in picture lbayer, as contents scale is always 1.
+    tilings_->tiling_at(i)->UpdateTilePriorities(tree,
+                                                 viewport_rect_in_layer_space,
+                                                 ideal_contents_scale_,
+                                                 current_frame_time_in_seconds,
+                                                 occlusion_in_content_space);
+  }
+
+  // Tile priorities were modified.
+  layer_tree_impl()->DidModifyTilePriorities();
+}
 
-  // Use visible_content_rect, unless it's empty. If it's empty, then
-  // try to inverse project the viewport into layer space and use that.
+gfx::Rect PictureLayerImpl::GetViewportForTilePriorityInContentSpace() const {
+  // If visible_rect_for_tile_priority_ is empty or
+  // viewport_rect_for_tile_priority_ is set to be different from the device
+  // viewport, try to inverse project the viewport into layer space and use
+  // that. Otherwise just use visible_rect_for_tile_priority_
   gfx::Rect visible_rect_in_content_space = visible_rect_for_tile_priority_;
-  if (visible_rect_in_content_space.IsEmpty()) {
-    gfx::Transform screen_to_layer(gfx::Transform::kSkipInitialization);
-    if (screen_space_transform_for_tile_priority_.GetInverse(
-            &screen_to_layer)) {
+
+  if (visible_rect_in_content_space.IsEmpty() ||
+      layer_tree_impl()->DeviceViewport() != viewport_rect_for_tile_priority_) {
+    gfx::Transform view_to_layer(gfx::Transform::kSkipInitialization);
+
+    if (screen_space_transform_for_tile_priority_.GetInverse(&view_to_layer)) {
+      // Transform from view space to content space.
       visible_rect_in_content_space =
           gfx::ToEnclosingRect(MathUtil::ProjectClippedRect(
-              screen_to_layer, gfx::Rect(viewport_size_for_tile_priority_)));
-      visible_rect_in_content_space.Intersect(gfx::Rect(content_bounds()));
+              view_to_layer, viewport_rect_for_tile_priority_));
     }
   }
+  return visible_rect_in_content_space;
+}
 
-  WhichTree tree =
-      layer_tree_impl()->IsActiveTree() ? ACTIVE_TREE : PENDING_TREE;
-
-  tilings_->UpdateTilePriorities(tree,
-                                 visible_rect_in_content_space,
-                                 contents_scale_x(),
-                                 current_frame_time_in_seconds);
-
-  if (layer_tree_impl()->IsPendingTree())
-    MarkVisibleResourcesAsRequired();
-
-  // Tile priorities were modified.
-  layer_tree_impl()->DidModifyTilePriorities();
+PictureLayerImpl* PictureLayerImpl::GetRecycledTwinLayer() {
+  // TODO(vmpstr): Maintain recycled twin as a member. crbug.com/407418
+  return static_cast<PictureLayerImpl*>(
+      layer_tree_impl()->FindRecycleTreeLayerById(id()));
 }
 
-void PictureLayerImpl::NotifyTileInitialized(const Tile* tile) {
+void PictureLayerImpl::NotifyTileStateChanged(const Tile* tile) {
   if (layer_tree_impl()->IsActiveTree()) {
     gfx::RectF layer_damage_rect =
         gfx::ScaleRect(tile->content_rect(), 1.f / tile->contents_scale());
@@ -448,101 +586,39 @@ void PictureLayerImpl::ReleaseResources() {
   layer_tree_impl()->set_needs_update_draw_properties();
 }
 
-void PictureLayerImpl::CalculateContentsScale(
-    float ideal_contents_scale,
-    float device_scale_factor,
-    float page_scale_factor,
-    float maximum_animation_contents_scale,
-    bool animating_transform_to_screen,
-    float* contents_scale_x,
-    float* contents_scale_y,
-    gfx::Size* content_bounds) {
-  DoPostCommitInitializationIfNeeded();
-
-  // This function sets valid raster scales and manages tilings, so tile
-  // priorities can now be updated.
-  should_update_tile_priorities_ = true;
-
-  if (!CanHaveTilings()) {
-    ideal_page_scale_ = page_scale_factor;
-    ideal_device_scale_ = device_scale_factor;
-    ideal_contents_scale_ = ideal_contents_scale;
-    ideal_source_scale_ =
-        ideal_contents_scale_ / ideal_page_scale_ / ideal_device_scale_;
-    *contents_scale_x = ideal_contents_scale_;
-    *contents_scale_y = ideal_contents_scale_;
-    *content_bounds = gfx::ToCeiledSize(gfx::ScaleSize(bounds(),
-                                                       ideal_contents_scale_,
-                                                       ideal_contents_scale_));
-    return;
-  }
-
-  float min_contents_scale = MinimumContentsScale();
-  DCHECK_GT(min_contents_scale, 0.f);
-  float min_page_scale = layer_tree_impl()->min_page_scale_factor();
-  DCHECK_GT(min_page_scale, 0.f);
-  float min_device_scale = 1.f;
-  float min_source_scale =
-      min_contents_scale / min_page_scale / min_device_scale;
-
-  float ideal_page_scale = page_scale_factor;
-  float ideal_device_scale = device_scale_factor;
-  float ideal_source_scale =
-      ideal_contents_scale / ideal_page_scale / ideal_device_scale;
-
-  ideal_contents_scale_ = std::max(ideal_contents_scale, min_contents_scale);
-  ideal_page_scale_ = ideal_page_scale;
-  ideal_device_scale_ = ideal_device_scale;
-  ideal_source_scale_ = std::max(ideal_source_scale, min_source_scale);
-
-  ManageTilings(animating_transform_to_screen,
-                maximum_animation_contents_scale);
-
-  // The content scale and bounds for a PictureLayerImpl is somewhat fictitious.
-  // There are (usually) several tilings at different scales.  However, the
-  // content bounds is the (integer!) space in which quads are generated.
-  // In order to guarantee that we can fill this integer space with any set of
-  // tilings (and then map back to floating point texture coordinates), the
-  // contents scale must be at least as large as the largest of the tilings.
-  float max_contents_scale = min_contents_scale;
-  for (size_t i = 0; i < tilings_->num_tilings(); ++i) {
-    const PictureLayerTiling* tiling = tilings_->tiling_at(i);
-    max_contents_scale = std::max(max_contents_scale, tiling->contents_scale());
-  }
-
-  *contents_scale_x = max_contents_scale;
-  *contents_scale_y = max_contents_scale;
-  *content_bounds = gfx::ToCeiledSize(
-      gfx::ScaleSize(bounds(), max_contents_scale, max_contents_scale));
-}
-
 skia::RefPtr<SkPicture> PictureLayerImpl::GetPicture() {
   return pile_->GetFlattenedPicture();
 }
 
 scoped_refptr<Tile> PictureLayerImpl::CreateTile(PictureLayerTiling* tiling,
                                                const gfx::Rect& content_rect) {
+  DCHECK(!pile_->is_solid_color());
   if (!pile_->CanRaster(tiling->contents_scale(), content_rect))
     return scoped_refptr<Tile>();
 
   int flags = 0;
-  if (is_using_lcd_text_)
-    flags |= Tile::USE_LCD_TEXT;
-  if (use_gpu_rasterization())
-    flags |= Tile::USE_GPU_RASTERIZATION;
+
+  // TODO(vmpstr): Revisit this. For now, enabling analysis means that we get as
+  // much savings on memory as we can. However, for some cases like ganesh or
+  // small layers, the amount of time we spend analyzing might not justify
+  // memory savings that we can get. Note that we don't handle solid color
+  // masks, so we shouldn't bother analyzing those.
+  // Bugs: crbug.com/397198, crbug.com/396908
+  if (!pile_->is_mask())
+    flags = Tile::USE_PICTURE_ANALYSIS;
+
   return layer_tree_impl()->tile_manager()->CreateTile(
       pile_.get(),
       content_rect.size(),
       content_rect,
-      contents_opaque() ? content_rect : gfx::Rect(),
       tiling->contents_scale(),
       id(),
       layer_tree_impl()->source_frame_number(),
       flags);
 }
 
-void PictureLayerImpl::UpdatePile(Tile* tile) {
-  tile->set_picture_pile(pile_);
+PicturePileImpl* PictureLayerImpl::GetPile() {
+  return pile_.get();
 }
 
 const Region* PictureLayerImpl::GetInvalidation() {
@@ -551,14 +627,17 @@ const Region* PictureLayerImpl::GetInvalidation() {
 
 const PictureLayerTiling* PictureLayerImpl::GetTwinTiling(
     const PictureLayerTiling* tiling) const {
-  if (!twin_layer_ ||
-      twin_layer_->use_gpu_rasterization() != use_gpu_rasterization())
+  if (!twin_layer_)
+    return NULL;
+  return twin_layer_->tilings_->TilingAtScale(tiling->contents_scale());
+}
+
+PictureLayerTiling* PictureLayerImpl::GetRecycledTwinTiling(
+    const PictureLayerTiling* tiling) {
+  PictureLayerImpl* recycled_twin = GetRecycledTwinLayer();
+  if (!recycled_twin || !recycled_twin->tilings_)
     return NULL;
-  for (size_t i = 0; i < twin_layer_->tilings_->num_tilings(); ++i)
-    if (twin_layer_->tilings_->tiling_at(i)->contents_scale() ==
-        tiling->contents_scale())
-      return twin_layer_->tilings_->tiling_at(i);
-  return NULL;
+  return recycled_twin->tilings_->TilingAtScale(tiling->contents_scale());
 }
 
 size_t PictureLayerImpl::GetMaxTilesForInterestArea() const {
@@ -566,9 +645,10 @@ size_t PictureLayerImpl::GetMaxTilesForInterestArea() const {
 }
 
 float PictureLayerImpl::GetSkewportTargetTimeInSeconds() const {
-  float skewport_target_time_in_frames = use_gpu_rasterization()
-                                             ? kGpuSkewportTargetTimeInFrames
-                                             : kCpuSkewportTargetTimeInFrames;
+  float skewport_target_time_in_frames =
+      layer_tree_impl()->use_gpu_rasterization()
+          ? kGpuSkewportTargetTimeInFrames
+          : kCpuSkewportTargetTimeInFrames;
   return skewport_target_time_in_frames *
          layer_tree_impl()->begin_impl_frame_interval().InSecondsF() *
          layer_tree_impl()->settings().skewport_target_time_multiplier;
@@ -582,18 +662,20 @@ int PictureLayerImpl::GetSkewportExtrapolationLimitInContentPixels() const {
 
 gfx::Size PictureLayerImpl::CalculateTileSize(
     const gfx::Size& content_bounds) const {
-  if (is_mask_) {
-    int max_size = layer_tree_impl()->MaxTextureSize();
-    return gfx::Size(
-        std::min(max_size, content_bounds.width()),
-        std::min(max_size, content_bounds.height()));
-  }
-
   int max_texture_size =
       layer_tree_impl()->resource_provider()->max_texture_size();
 
+  if (pile_->is_mask()) {
+    // Masks are not tiled, so if we can't cover the whole mask with one tile,
+    // don't make any tiles at all. Returning an empty size signals this.
+    if (content_bounds.width() > max_texture_size ||
+        content_bounds.height() > max_texture_size)
+      return gfx::Size();
+    return content_bounds;
+  }
+
   gfx::Size default_tile_size = layer_tree_impl()->settings().default_tile_size;
-  if (use_gpu_rasterization()) {
+  if (layer_tree_impl()->use_gpu_rasterization()) {
     // TODO(ernstm) crbug.com/365877: We need a unified way to override the
     // default-tile-size.
     default_tile_size =
@@ -626,15 +708,10 @@ gfx::Size PictureLayerImpl::CalculateTileSize(
     int height = std::min(
         std::max(max_untiled_content_size.height(), default_tile_size.height()),
         content_bounds.height());
-    // Round width and height up to the closest multiple of 64, or 56 if
-    // we should avoid power-of-two textures. This helps reduce the number
-    // of different textures sizes to help recycling, and also keeps all
-    // textures multiple-of-eight, which is preferred on some drivers (IMG).
-    bool avoid_pow2 =
-        layer_tree_impl()->GetRendererCapabilities().avoid_pow2_textures;
-    int round_up_to = avoid_pow2 ? 56 : 64;
-    width = RoundUp(width, round_up_to);
-    height = RoundUp(height, round_up_to);
+    // Round up to the closest multiple of 64. This improves recycling and
+    // avoids odd texture sizes.
+    width = RoundUp(width, 64);
+    height = RoundUp(height, 64);
     return gfx::Size(width, height);
   }
 
@@ -642,11 +719,10 @@ gfx::Size PictureLayerImpl::CalculateTileSize(
 }
 
 void PictureLayerImpl::SyncFromActiveLayer(const PictureLayerImpl* other) {
+  TRACE_EVENT0("cc", "SyncFromActiveLayer");
   DCHECK(!other->needs_post_commit_initialization_);
   DCHECK(other->tilings_);
 
-  UpdateLCDTextStatus(other->is_using_lcd_text_);
-
   if (!DrawsContent()) {
     RemoveAllTilings();
     return;
@@ -658,36 +734,25 @@ void PictureLayerImpl::SyncFromActiveLayer(const PictureLayerImpl* other) {
   raster_contents_scale_ = other->raster_contents_scale_;
   low_res_raster_contents_scale_ = other->low_res_raster_contents_scale_;
 
-  // Add synthetic invalidations for any recordings that were dropped.  As
-  // tiles are updated to point to this new pile, this will force the dropping
-  // of tiles that can no longer be rastered.  This is not ideal, but is a
-  // trade-off for memory (use the same pile as much as possible, by switching
-  // during DidBecomeActive) and for time (don't bother checking every tile
-  // during activation to see if the new pile can still raster it).
-  for (int x = 0; x < pile_->num_tiles_x(); ++x) {
-    for (int y = 0; y < pile_->num_tiles_y(); ++y) {
-      bool previously_had = other->pile_->HasRecordingAt(x, y);
-      bool now_has = pile_->HasRecordingAt(x, y);
-      if (now_has || !previously_had)
-        continue;
-      gfx::Rect layer_rect = pile_->tile_bounds(x, y);
-      invalidation_.Union(layer_rect);
-    }
-  }
-
-  // Union in the other newly exposed regions as invalid.
-  Region difference_region = Region(gfx::Rect(bounds()));
-  difference_region.Subtract(gfx::Rect(other->bounds()));
-  invalidation_.Union(difference_region);
-
+  bool synced_high_res_tiling = false;
   if (CanHaveTilings()) {
-    tilings_->SyncTilings(
+    synced_high_res_tiling = tilings_->SyncTilings(
         *other->tilings_, bounds(), invalidation_, MinimumContentsScale());
   } else {
     RemoveAllTilings();
   }
 
-  SanityCheckTilingState();
+  // If our MinimumContentsScale has changed to prevent the twin's high res
+  // tiling from being synced, we should reset the raster scale and let it be
+  // recalculated (1) again. This can happen if our bounds shrink to the point
+  // where min contents scale grows.
+  // (1) - TODO(vmpstr) Instead of hoping that this will be recalculated, we
+  // should refactor this code a little bit and actually recalculate this.
+  // However, this is a larger undertaking, so this will work for now.
+  if (!synced_high_res_tiling)
+    ResetRasterScale();
+  else
+    SanityCheckTilingState();
 }
 
 void PictureLayerImpl::SyncTiling(
@@ -702,31 +767,27 @@ void PictureLayerImpl::SyncTiling(
   // we can create tiles for this tiling immediately.
   if (!layer_tree_impl()->needs_update_draw_properties() &&
       should_update_tile_priorities_) {
-    UpdateTilePriorities();
+    // TODO(danakj): Add a DCHECK() that we are not using occlusion tracking
+    // when we stop using the pending tree in the browser compositor. If we want
+    // to support occlusion tracking here, we need to dirty the draw properties
+    // or save occlusion as a draw property.
+    UpdateTilePriorities(Occlusion());
   }
 }
 
-void PictureLayerImpl::SetIsMask(bool is_mask) {
-  if (is_mask_ == is_mask)
-    return;
-  is_mask_ = is_mask;
-  if (tilings_)
-    tilings_->RemoveAllTiles();
-}
-
 ResourceProvider::ResourceId PictureLayerImpl::ContentsResourceId() const {
   gfx::Rect content_rect(content_bounds());
-  float scale = contents_scale_x();
   PictureLayerTilingSet::CoverageIterator iter(
-      tilings_.get(), scale, content_rect, ideal_contents_scale_);
+      tilings_.get(), 1.f, content_rect, ideal_contents_scale_);
 
   // Mask resource not ready yet.
   if (!iter || !*iter)
     return 0;
 
   // Masks only supported if they fit on exactly one tile.
-  if (iter.geometry_rect() != content_rect)
-    return 0;
+  DCHECK(iter.geometry_rect() == content_rect)
+      << "iter rect " << iter.geometry_rect().ToString() << " content rect "
+      << content_rect.ToString();
 
   const ManagedTileState::TileVersion& tile_version =
       iter->GetTileVersionForDrawing();
@@ -739,7 +800,6 @@ ResourceProvider::ResourceId PictureLayerImpl::ContentsResourceId() const {
 
 void PictureLayerImpl::MarkVisibleResourcesAsRequired() const {
   DCHECK(layer_tree_impl()->IsPendingTree());
-  DCHECK(!layer_tree_impl()->needs_update_draw_properties());
   DCHECK(ideal_contents_scale_);
   DCHECK_GT(tilings_->num_tilings(), 0u);
 
@@ -747,7 +807,16 @@ void PictureLayerImpl::MarkVisibleResourcesAsRequired() const {
   // be ready to draw in order to activate without flashing content from a
   // higher res on the active tree to a lower res on the pending tree.
 
-  gfx::Rect rect(visible_content_rect());
+  // First, early out for layers with no visible content.
+  if (visible_content_rect().IsEmpty())
+    return;
+
+  // Only mark tiles inside the viewport for tile priority as required for
+  // activation. This viewport is normally the same as the draw viewport but
+  // can be independently overridden by embedders like Android WebView with
+  // SetExternalDrawConstraints.
+  gfx::Rect rect = GetViewportForTilePriorityInContentSpace();
+  rect.Intersect(visible_content_rect());
 
   float min_acceptable_scale =
       std::min(raster_contents_scale_, ideal_contents_scale_);
@@ -785,10 +854,7 @@ void PictureLayerImpl::MarkVisibleResourcesAsRequired() const {
       high_res = tiling;
       continue;
     }
-    for (PictureLayerTiling::CoverageIterator iter(tiling,
-                                                   contents_scale_x(),
-                                                   rect);
-         iter;
+    for (PictureLayerTiling::CoverageIterator iter(tiling, 1.f, rect); iter;
          ++iter) {
       if (!*iter || !iter->IsReadyToDraw())
         continue;
@@ -806,58 +872,61 @@ void PictureLayerImpl::MarkVisibleResourcesAsRequired() const {
   const PictureLayerTiling* twin_high_res = NULL;
   const PictureLayerTiling* twin_low_res = NULL;
 
-  // As a simplification, only allow activating to skip twin tiles that the
-  // active layer is also missing when both this layer and its twin have 2
-  // tilings (high and low).  This avoids having to iterate/track coverage of
-  // non-ideal tilings during the last draw call on the active layer.
-  if (high_res && low_res && tilings_->num_tilings() == 2 &&
-      twin_layer_ && twin_layer_->tilings_->num_tilings() == 2) {
-    twin_low_res = GetTwinTiling(low_res);
-    if (twin_low_res)
-      twin_high_res = GetTwinTiling(high_res);
-  }
-  // If this layer and its twin have different bounds or transforms, then don't
-  // compare them and only allow activating to high res tiles, since tiles on
-  // each layer will occupy different areas of the screen.
-  if (!twin_high_res || !twin_low_res ||
-      twin_layer_->layer_tree_impl()->RequiresHighResToDraw() ||
-      bounds() != twin_layer_->bounds() ||
-      draw_properties().screen_space_transform !=
-          twin_layer_->draw_properties().screen_space_transform) {
-    twin_high_res = NULL;
-    twin_low_res = NULL;
+  if (twin_layer_) {
+    // As a simplification, only allow activating to skip twin tiles that the
+    // active layer is also missing when both this layer and its twin have
+    // "simple" sets of tilings: only 2 tilings (high and low) or only 1 high
+    // res tiling. This avoids having to iterate/track coverage of non-ideal
+    // tilings during the last draw call on the active layer.
+    if (tilings_->num_tilings() <= 2 &&
+        twin_layer_->tilings_->num_tilings() <= tilings_->num_tilings()) {
+      twin_low_res = low_res ? GetTwinTiling(low_res) : NULL;
+      twin_high_res = high_res ? GetTwinTiling(high_res) : NULL;
+    }
+
+    // If this layer and its twin have different transforms, then don't compare
+    // them and only allow activating to high res tiles, since tiles on each
+    // layer will be in different places on screen.
+    if (twin_layer_->layer_tree_impl()->RequiresHighResToDraw() ||
+        bounds() != twin_layer_->bounds() ||
+        draw_properties().screen_space_transform !=
+            twin_layer_->draw_properties().screen_space_transform) {
+      twin_high_res = NULL;
+      twin_low_res = NULL;
+    }
   }
 
   // As a second pass, mark as required any visible high res tiles not filled in
   // by acceptable non-ideal tiles from the first pass.
   if (MarkVisibleTilesAsRequired(
-      high_res, twin_high_res, contents_scale_x(), rect, missing_region)) {
+          high_res, twin_high_res, rect, missing_region)) {
     // As an optional third pass, if a high res tile was skipped because its
     // twin was also missing, then fall back to mark low res tiles as required
     // in case the active twin is substituting those for missing high res
-    // content.
-    MarkVisibleTilesAsRequired(
-        low_res, twin_low_res, contents_scale_x(), rect, missing_region);
+    // content. Only suitable, when low res is enabled.
+    if (low_res) {
+      MarkVisibleTilesAsRequired(low_res, twin_low_res, rect, missing_region);
+    }
   }
 }
 
 bool PictureLayerImpl::MarkVisibleTilesAsRequired(
     PictureLayerTiling* tiling,
     const PictureLayerTiling* optional_twin_tiling,
-    float contents_scale,
     const gfx::Rect& rect,
     const Region& missing_region) const {
   bool twin_had_missing_tile = false;
-  for (PictureLayerTiling::CoverageIterator iter(tiling,
-                                                 contents_scale,
-                                                 rect);
-       iter;
+  for (PictureLayerTiling::CoverageIterator iter(tiling, 1.f, rect); iter;
        ++iter) {
     Tile* tile = *iter;
     // A null tile (i.e. missing recording) can just be skipped.
     if (!tile)
       continue;
 
+    // If the tile is occluded, don't mark it as required for activation.
+    if (tile->is_occluded(PENDING_TREE))
+      continue;
+
     // If the missing region doesn't cover it, this tile is fully
     // covered by acceptable tiles at other scales.
     if (!missing_region.Intersects(iter.geometry_rect()))
@@ -869,7 +938,10 @@ bool PictureLayerImpl::MarkVisibleTilesAsRequired(
     if (optional_twin_tiling) {
       Tile* twin_tile = optional_twin_tiling->TileAt(iter.i(), iter.j());
       if (!twin_tile || twin_tile == tile) {
-        twin_had_missing_tile = true;
+        // However if the shared tile is being used on the active tree, then
+        // there's no missing content in this place, and low res is not needed.
+        if (!twin_tile || !twin_tile->IsReadyToDraw())
+          twin_had_missing_tile = true;
         continue;
       }
     }
@@ -909,14 +981,16 @@ PictureLayerTiling* PictureLayerImpl::AddTiling(float contents_scale) {
 
   DCHECK(pile_->HasRecordings());
 
-  if (twin_layer_ &&
-      twin_layer_->use_gpu_rasterization() == use_gpu_rasterization())
+  if (twin_layer_)
     twin_layer_->SyncTiling(tiling);
 
   return tiling;
 }
 
 void PictureLayerImpl::RemoveTiling(float contents_scale) {
+  if (!tilings_ || tilings_->num_tilings() == 0)
+    return;
+
   for (size_t i = 0; i < tilings_->num_tilings(); ++i) {
     PictureLayerTiling* tiling = tilings_->tiling_at(i);
     if (tiling->contents_scale() == contents_scale) {
@@ -946,38 +1020,7 @@ inline float PositiveRatio(float float1, float float2) {
 
 }  // namespace
 
-void PictureLayerImpl::ManageTilings(bool animating_transform_to_screen,
-                                     float maximum_animation_contents_scale) {
-  DCHECK(ideal_contents_scale_);
-  DCHECK(ideal_page_scale_);
-  DCHECK(ideal_device_scale_);
-  DCHECK(ideal_source_scale_);
-  DCHECK(CanHaveTilings());
-  DCHECK(!needs_post_commit_initialization_);
-
-  bool change_target_tiling =
-      raster_page_scale_ == 0.f ||
-      raster_device_scale_ == 0.f ||
-      raster_source_scale_ == 0.f ||
-      raster_contents_scale_ == 0.f ||
-      low_res_raster_contents_scale_ == 0.f ||
-      ShouldAdjustRasterScale(animating_transform_to_screen);
-
-  if (tilings_->num_tilings() == 0) {
-    DCHECK(change_target_tiling)
-        << "A layer with no tilings shouldn't have valid raster scales";
-  }
-
-  if (change_target_tiling) {
-    RecalculateRasterScales(animating_transform_to_screen,
-                            maximum_animation_contents_scale);
-  }
-
-  was_animating_transform_to_screen_ = animating_transform_to_screen;
-
-  if (!change_target_tiling)
-    return;
-
+void PictureLayerImpl::AddTilingsForRasterScale() {
   PictureLayerTiling* high_res = NULL;
   PictureLayerTiling* low_res = NULL;
 
@@ -1005,9 +1048,9 @@ void PictureLayerImpl::ManageTilings(bool animating_transform_to_screen,
   // prevents wastefully creating a paired low res tiling for every new high res
   // tiling during a pinch or a CSS animation.
   bool is_pinching = layer_tree_impl()->PinchGestureActive();
-  if (ShouldHaveLowResTiling() && !is_pinching &&
-      !animating_transform_to_screen &&
-      !low_res && low_res != high_res)
+  if (layer_tree_impl()->create_low_res_tiling() && !is_pinching &&
+      !draw_properties().screen_space_transform_is_animating && !low_res &&
+      low_res != high_res)
     low_res = AddTiling(low_res_raster_contents_scale_);
 
   // Set low-res if we have one.
@@ -1022,9 +1065,14 @@ void PictureLayerImpl::ManageTilings(bool animating_transform_to_screen,
   SanityCheckTilingState();
 }
 
-bool PictureLayerImpl::ShouldAdjustRasterScale(
-    bool animating_transform_to_screen) const {
-  if (was_animating_transform_to_screen_ != animating_transform_to_screen)
+bool PictureLayerImpl::ShouldAdjustRasterScale() const {
+  if (was_screen_space_transform_animating_ !=
+      draw_properties().screen_space_transform_is_animating)
+    return true;
+
+  if (draw_properties().screen_space_transform_is_animating &&
+      raster_contents_scale_ != ideal_contents_scale_ &&
+      ShouldAdjustRasterScaleDuringScaleAnimations())
     return true;
 
   bool is_pinching = layer_tree_impl()->PinchGestureActive();
@@ -1050,7 +1098,8 @@ bool PictureLayerImpl::ShouldAdjustRasterScale(
 
   // When the source scale changes we want to match it, but not when animating
   // or when we've fixed the scale in place.
-  if (!animating_transform_to_screen && !raster_source_scale_is_fixed_ &&
+  if (!draw_properties().screen_space_transform_is_animating &&
+      !raster_source_scale_is_fixed_ &&
       raster_source_scale_ != ideal_source_scale_)
     return true;
 
@@ -1072,9 +1121,7 @@ float PictureLayerImpl::SnappedContentsScale(float scale) {
   return snapped_contents_scale;
 }
 
-void PictureLayerImpl::RecalculateRasterScales(
-    bool animating_transform_to_screen,
-    float maximum_animation_contents_scale) {
+void PictureLayerImpl::RecalculateRasterScales() {
   float old_raster_contents_scale = raster_contents_scale_;
   float old_raster_page_scale = raster_page_scale_;
   float old_raster_source_scale = raster_source_scale_;
@@ -1087,15 +1134,16 @@ void PictureLayerImpl::RecalculateRasterScales(
   // If we're not animating, or leaving an animation, and the
   // ideal_source_scale_ changes, then things are unpredictable, and we fix
   // the raster_source_scale_ in place.
-  if (old_raster_source_scale && !animating_transform_to_screen &&
-      !was_animating_transform_to_screen_ &&
+  if (old_raster_source_scale &&
+      !draw_properties().screen_space_transform_is_animating &&
+      !was_screen_space_transform_animating_ &&
       old_raster_source_scale != ideal_source_scale_)
     raster_source_scale_is_fixed_ = true;
 
   // TODO(danakj): Adjust raster source scale closer to ideal source scale at
   // a throttled rate. Possibly make use of invalidation_.IsEmpty() on pending
   // tree. This will allow CSS scale changes to get re-rastered at an
-  // appropriate rate.
+  // appropriate rate. (crbug.com/413636)
   if (raster_source_scale_is_fixed_) {
     raster_contents_scale_ /= raster_source_scale_;
     raster_source_scale_ = 1.f;
@@ -1121,13 +1169,24 @@ void PictureLayerImpl::RecalculateRasterScales(
   raster_contents_scale_ =
       std::max(raster_contents_scale_, MinimumContentsScale());
 
-  // Since we're not re-rasterizing during animation, rasterize at the maximum
+  // If we're not re-rasterizing during animation, rasterize at the maximum
   // scale that will occur during the animation, if the maximum scale is
-  // known.
-  if (animating_transform_to_screen) {
-    if (maximum_animation_contents_scale > 0.f) {
+  // known. However, to avoid excessive memory use, don't rasterize at a scale
+  // at which this layer would become larger than the viewport.
+  if (draw_properties().screen_space_transform_is_animating &&
+      !ShouldAdjustRasterScaleDuringScaleAnimations()) {
+    bool can_raster_at_maximum_scale = false;
+    if (draw_properties().maximum_animation_contents_scale > 0.f) {
+      gfx::Size bounds_at_maximum_scale = gfx::ToCeiledSize(gfx::ScaleSize(
+          bounds(), draw_properties().maximum_animation_contents_scale));
+      if (bounds_at_maximum_scale.GetArea() <=
+          layer_tree_impl()->device_viewport_size().GetArea())
+        can_raster_at_maximum_scale = true;
+    }
+    if (can_raster_at_maximum_scale) {
       raster_contents_scale_ =
-          std::max(raster_contents_scale_, maximum_animation_contents_scale);
+          std::max(raster_contents_scale_,
+                   draw_properties().maximum_animation_contents_scale);
     } else {
       raster_contents_scale_ =
           std::max(raster_contents_scale_,
@@ -1135,13 +1194,14 @@ void PictureLayerImpl::RecalculateRasterScales(
     }
   }
 
-  // If this layer would only create one tile at this content scale,
+  // If this layer would create zero or one tiles at this content scale,
   // don't create a low res tiling.
   gfx::Size content_bounds =
       gfx::ToCeiledSize(gfx::ScaleSize(bounds(), raster_contents_scale_));
   gfx::Size tile_size = CalculateTileSize(content_bounds);
-  if (tile_size.width() >= content_bounds.width() &&
-      tile_size.height() >= content_bounds.height()) {
+  bool tile_covers_bounds = tile_size.width() >= content_bounds.width() &&
+                            tile_size.height() >= content_bounds.height();
+  if (tile_size.IsEmpty() || tile_covers_bounds) {
     low_res_raster_contents_scale_ = raster_contents_scale_;
     return;
   }
@@ -1163,15 +1223,22 @@ void PictureLayerImpl::CleanUpTilingsOnActiveLayer(
       raster_contents_scale_, ideal_contents_scale_);
   float max_acceptable_high_res_scale = std::max(
       raster_contents_scale_, ideal_contents_scale_);
+  float twin_low_res_scale = 0.f;
 
   PictureLayerImpl* twin = twin_layer_;
-  if (twin) {
+  if (twin && twin->CanHaveTilings()) {
     min_acceptable_high_res_scale = std::min(
         min_acceptable_high_res_scale,
         std::min(twin->raster_contents_scale_, twin->ideal_contents_scale_));
     max_acceptable_high_res_scale = std::max(
         max_acceptable_high_res_scale,
         std::max(twin->raster_contents_scale_, twin->ideal_contents_scale_));
+
+    for (size_t i = 0; i < twin->tilings_->num_tilings(); ++i) {
+      PictureLayerTiling* tiling = twin->tilings_->tiling_at(i);
+      if (tiling->resolution() == LOW_RESOLUTION)
+        twin_low_res_scale = tiling->contents_scale();
+    }
   }
 
   std::vector<PictureLayerTiling*> to_remove;
@@ -1185,8 +1252,11 @@ void PictureLayerImpl::CleanUpTilingsOnActiveLayer(
       continue;
 
     // Keep low resolution tilings, if the layer should have them.
-    if (tiling->resolution() == LOW_RESOLUTION && ShouldHaveLowResTiling())
-      continue;
+    if (layer_tree_impl()->create_low_res_tiling()) {
+      if (tiling->resolution() == LOW_RESOLUTION ||
+          tiling->contents_scale() == twin_low_res_scale)
+        continue;
+    }
 
     // Don't remove tilings that are being used (and thus would cause a flash.)
     if (std::find(used_tilings.begin(), used_tilings.end(), tiling) !=
@@ -1196,18 +1266,27 @@ void PictureLayerImpl::CleanUpTilingsOnActiveLayer(
     to_remove.push_back(tiling);
   }
 
+  if (to_remove.empty())
+    return;
+
+  PictureLayerImpl* recycled_twin = GetRecycledTwinLayer();
+  // Remove tilings on this tree and the twin tree.
   for (size_t i = 0; i < to_remove.size(); ++i) {
     const PictureLayerTiling* twin_tiling = GetTwinTiling(to_remove[i]);
     // Only remove tilings from the twin layer if they have
     // NON_IDEAL_RESOLUTION.
     if (twin_tiling && twin_tiling->resolution() == NON_IDEAL_RESOLUTION)
       twin->RemoveTiling(to_remove[i]->contents_scale());
+    // Remove the tiling from the recycle tree. Note that we ignore resolution,
+    // since we don't need to maintain high/low res on the recycle tree.
+    if (recycled_twin)
+      recycled_twin->RemoveTiling(to_remove[i]->contents_scale());
     // TODO(enne): temporary sanity CHECK for http://crbug.com/358350
     CHECK_NE(HIGH_RESOLUTION, to_remove[i]->resolution());
     tilings_->Remove(to_remove[i]);
   }
-  DCHECK_GT(tilings_->num_tilings(), 0u);
 
+  DCHECK_GT(tilings_->num_tilings(), 0u);
   SanityCheckTilingState();
 }
 
@@ -1225,18 +1304,6 @@ float PictureLayerImpl::MinimumContentsScale() const {
   return std::max(1.f / min_dimension, setting_min);
 }
 
-void PictureLayerImpl::UpdateLCDTextStatus(bool new_status) {
-  // Once this layer is not using lcd text, don't switch back.
-  if (!is_using_lcd_text_)
-    return;
-
-  if (is_using_lcd_text_ == new_status)
-    return;
-
-  is_using_lcd_text_ = new_status;
-  tilings_->SetCanUseLCDText(is_using_lcd_text_);
-}
-
 void PictureLayerImpl::ResetRasterScale() {
   raster_page_scale_ = 0.f;
   raster_device_scale_ = 0.f;
@@ -1251,6 +1318,8 @@ void PictureLayerImpl::ResetRasterScale() {
 }
 
 bool PictureLayerImpl::CanHaveTilings() const {
+  if (pile_->is_solid_color())
+    return false;
   if (!DrawsContent())
     return false;
   if (!pile_->HasRecordings())
@@ -1268,6 +1337,10 @@ bool PictureLayerImpl::CanHaveTilingWithScale(float contents_scale) const {
 
 void PictureLayerImpl::SanityCheckTilingState() const {
 #if DCHECK_IS_ON
+  // Recycle tree doesn't have any restrictions.
+  if (layer_tree_impl()->IsRecycleTree())
+    return;
+
   if (!CanHaveTilings()) {
     DCHECK_EQ(0u, tilings_->num_tilings());
     return;
@@ -1281,6 +1354,53 @@ void PictureLayerImpl::SanityCheckTilingState() const {
 #endif
 }
 
+bool PictureLayerImpl::ShouldAdjustRasterScaleDuringScaleAnimations() const {
+  if (!layer_tree_impl()->use_gpu_rasterization())
+    return false;
+
+  // Re-rastering text at different scales using GPU rasterization causes
+  // texture uploads for glyphs at each scale (see crbug.com/366225). To
+  // workaround this performance issue, we don't re-rasterize layers with
+  // text during scale animations.
+  // TODO(ajuma): Remove this workaround once text can be efficiently
+  // re-rastered at different scales (e.g. by using distance-field fonts).
+  if (pile_->has_text())
+    return false;
+
+  return true;
+}
+
+float PictureLayerImpl::MaximumTilingContentsScale() const {
+  float max_contents_scale = MinimumContentsScale();
+  for (size_t i = 0; i < tilings_->num_tilings(); ++i) {
+    const PictureLayerTiling* tiling = tilings_->tiling_at(i);
+    max_contents_scale = std::max(max_contents_scale, tiling->contents_scale());
+  }
+  return max_contents_scale;
+}
+
+void PictureLayerImpl::UpdateIdealScales() {
+  DCHECK(CanHaveTilings());
+
+  float min_contents_scale = MinimumContentsScale();
+  DCHECK_GT(min_contents_scale, 0.f);
+  float min_page_scale = layer_tree_impl()->min_page_scale_factor();
+  DCHECK_GT(min_page_scale, 0.f);
+  float min_device_scale = 1.f;
+  float min_source_scale =
+      min_contents_scale / min_page_scale / min_device_scale;
+
+  float ideal_page_scale = draw_properties().page_scale_factor;
+  float ideal_device_scale = draw_properties().device_scale_factor;
+  float ideal_source_scale = draw_properties().ideal_contents_scale /
+                             ideal_page_scale / ideal_device_scale;
+  ideal_contents_scale_ =
+      std::max(draw_properties().ideal_contents_scale, min_contents_scale);
+  ideal_page_scale_ = draw_properties().page_scale_factor;
+  ideal_device_scale_ = draw_properties().device_scale_factor;
+  ideal_source_scale_ = std::max(ideal_source_scale, min_source_scale);
+}
+
 void PictureLayerImpl::GetDebugBorderProperties(
     SkColor* color,
     float* width) const {
@@ -1288,33 +1408,59 @@ void PictureLayerImpl::GetDebugBorderProperties(
   *width = DebugColors::TiledContentLayerBorderWidth(layer_tree_impl());
 }
 
-void PictureLayerImpl::AsValueInto(base::DictionaryValue* state) const {
+void PictureLayerImpl::GetAllTilesForTracing(
+    std::set<const Tile*>* tiles) const {
+  if (!tilings_)
+    return;
+
+  for (size_t i = 0; i < tilings_->num_tilings(); ++i)
+    tilings_->tiling_at(i)->GetAllTilesForTracing(tiles);
+}
+
+void PictureLayerImpl::AsValueInto(base::debug::TracedValue* state) const {
   const_cast<PictureLayerImpl*>(this)->DoPostCommitInitializationIfNeeded();
   LayerImpl::AsValueInto(state);
   state->SetDouble("ideal_contents_scale", ideal_contents_scale_);
-  state->SetDouble("geometry_contents_scale", contents_scale_x());
-  state->Set("tilings", tilings_->AsValue().release());
-  state->Set("pictures", pile_->AsValue().release());
-  state->Set("invalidation", invalidation_.AsValue().release());
+  state->SetDouble("geometry_contents_scale", MaximumTilingContentsScale());
+  state->BeginArray("tilings");
+  tilings_->AsValueInto(state);
+  state->EndArray();
 
-  scoped_ptr<base::ListValue> coverage_tiles(new base::ListValue);
+  state->BeginArray("tile_priority_rect");
+  MathUtil::AddToTracedValue(GetViewportForTilePriorityInContentSpace(), state);
+  state->EndArray();
+
+  state->BeginArray("visible_rect");
+  MathUtil::AddToTracedValue(visible_content_rect(), state);
+  state->EndArray();
+
+  state->BeginArray("pictures");
+  pile_->AsValueInto(state);
+  state->EndArray();
+
+  state->BeginArray("invalidation");
+  invalidation_.AsValueInto(state);
+  state->EndArray();
+
+  state->BeginArray("coverage_tiles");
   for (PictureLayerTilingSet::CoverageIterator iter(tilings_.get(),
-                                                    contents_scale_x(),
+                                                    1.f,
                                                     gfx::Rect(content_bounds()),
                                                     ideal_contents_scale_);
        iter;
        ++iter) {
-    scoped_ptr<base::DictionaryValue> tile_data(new base::DictionaryValue);
-    tile_data->Set("geometry_rect",
-                   MathUtil::AsValue(iter.geometry_rect()).release());
+    state->BeginDictionary();
+
+    state->BeginArray("geometry_rect");
+    MathUtil::AddToTracedValue(iter.geometry_rect(), state);
+    state->EndArray();
+
     if (*iter)
-      tile_data->Set("tile", TracedValue::CreateIDRef(*iter).release());
+      TracedValue::SetIDRef(*iter, state, "tile");
 
-    coverage_tiles->Append(tile_data.release());
+    state->EndDictionary();
   }
-  state->Set("coverage_tiles", coverage_tiles.release());
-  state->SetBoolean("is_using_lcd_text", is_using_lcd_text_);
-  state->SetBoolean("using_gpu_rasterization", use_gpu_rasterization());
+  state->EndArray();
 }
 
 size_t PictureLayerImpl::GPUMemoryUsageInBytes() const {
@@ -1334,21 +1480,69 @@ bool PictureLayerImpl::IsOnActiveOrPendingTree() const {
   return !layer_tree_impl()->IsRecycleTree();
 }
 
+bool PictureLayerImpl::HasValidTilePriorities() const {
+  return IsOnActiveOrPendingTree() && IsDrawnRenderSurfaceLayerListMember();
+}
+
+bool PictureLayerImpl::AllTilesRequiredForActivationAreReadyToDraw() const {
+  if (!layer_tree_impl()->IsPendingTree())
+    return true;
+
+  if (!HasValidTilePriorities())
+    return true;
+
+  if (!tilings_)
+    return true;
+
+  if (visible_content_rect().IsEmpty())
+    return true;
+
+  for (size_t i = 0; i < tilings_->num_tilings(); ++i) {
+    PictureLayerTiling* tiling = tilings_->tiling_at(i);
+    if (tiling->resolution() != HIGH_RESOLUTION &&
+        tiling->resolution() != LOW_RESOLUTION)
+      continue;
+
+    gfx::Rect rect(visible_content_rect());
+    for (PictureLayerTiling::CoverageIterator iter(tiling, 1.f, rect); iter;
+         ++iter) {
+      const Tile* tile = *iter;
+      // A null tile (i.e. missing recording) can just be skipped.
+      if (!tile)
+        continue;
+
+      if (tile->required_for_activation() && !tile->IsReadyToDraw())
+        return false;
+    }
+  }
+
+  return true;
+}
+
 PictureLayerImpl::LayerRasterTileIterator::LayerRasterTileIterator()
-    : layer_(NULL) {}
+    : layer_(NULL), current_stage_(arraysize(stages_)) {
+}
 
 PictureLayerImpl::LayerRasterTileIterator::LayerRasterTileIterator(
     PictureLayerImpl* layer,
     bool prioritize_low_res)
     : layer_(layer), current_stage_(0) {
   DCHECK(layer_);
+
+  // Early out if the layer has no tilings.
   if (!layer_->tilings_ || !layer_->tilings_->num_tilings()) {
     current_stage_ = arraysize(stages_);
     return;
   }
 
-  WhichTree tree =
-      layer_->layer_tree_impl()->IsActiveTree() ? ACTIVE_TREE : PENDING_TREE;
+  // Tiles without valid priority are treated as having lowest priority and
+  // never considered for raster.
+  if (!layer_->HasValidTilePriorities()) {
+    current_stage_ = arraysize(stages_);
+    return;
+  }
+
+  WhichTree tree = layer_->GetTree();
 
   // Find high and low res tilings and initialize the iterators.
   for (size_t i = 0; i < layer_->tilings_->num_tilings(); ++i) {
@@ -1387,13 +1581,13 @@ PictureLayerImpl::LayerRasterTileIterator::LayerRasterTileIterator(
   IteratorType index = stages_[current_stage_].iterator_type;
   TilePriority::PriorityBin tile_type = stages_[current_stage_].tile_type;
   if (!iterators_[index] || iterators_[index].get_type() != tile_type)
-    ++(*this);
+    AdvanceToNextStage();
 }
 
 PictureLayerImpl::LayerRasterTileIterator::~LayerRasterTileIterator() {}
 
 PictureLayerImpl::LayerRasterTileIterator::operator bool() const {
-  return layer_ && static_cast<size_t>(current_stage_) < arraysize(stages_);
+  return current_stage_ < arraysize(stages_);
 }
 
 PictureLayerImpl::LayerRasterTileIterator&
@@ -1403,23 +1597,13 @@ operator++() {
   TilePriority::PriorityBin tile_type = stages_[current_stage_].tile_type;
 
   // First advance the iterator.
-  if (iterators_[index])
-    ++iterators_[index];
-
-  if (iterators_[index] && iterators_[index].get_type() == tile_type)
-    return *this;
+  DCHECK(iterators_[index]);
+  DCHECK(iterators_[index].get_type() == tile_type);
+  ++iterators_[index];
 
-  // Next, advance the stage.
-  int stage_count = arraysize(stages_);
-  ++current_stage_;
-  while (current_stage_ < stage_count) {
-    index = stages_[current_stage_].iterator_type;
-    tile_type = stages_[current_stage_].tile_type;
+  if (!iterators_[index] || iterators_[index].get_type() != tile_type)
+    AdvanceToNextStage();
 
-    if (iterators_[index] && iterators_[index].get_type() == tile_type)
-      break;
-    ++current_stage_;
-  }
   return *this;
 }
 
@@ -1433,127 +1617,180 @@ Tile* PictureLayerImpl::LayerRasterTileIterator::operator*() {
   return *iterators_[index];
 }
 
-PictureLayerImpl::LayerEvictionTileIterator::LayerEvictionTileIterator()
-    : iterator_index_(0),
-      iteration_stage_(TilePriority::EVENTUALLY),
-      required_for_activation_(false),
-      layer_(NULL) {}
-
-PictureLayerImpl::LayerEvictionTileIterator::LayerEvictionTileIterator(
-    PictureLayerImpl* layer,
-    TreePriority tree_priority)
-    : iterator_index_(0),
-      iteration_stage_(TilePriority::EVENTUALLY),
-      required_for_activation_(false),
-      layer_(layer) {
-  if (!layer_->tilings_ || !layer_->tilings_->num_tilings())
-    return;
-
-  size_t high_res_tiling_index = layer_->tilings_->num_tilings();
-  size_t low_res_tiling_index = layer_->tilings_->num_tilings();
-  for (size_t i = 0; i < layer_->tilings_->num_tilings(); ++i) {
-    PictureLayerTiling* tiling = layer_->tilings_->tiling_at(i);
-    if (tiling->resolution() == HIGH_RESOLUTION)
-      high_res_tiling_index = i;
-    else if (tiling->resolution() == LOW_RESOLUTION)
-      low_res_tiling_index = i;
-  }
-
-  iterators_.reserve(layer_->tilings_->num_tilings());
+const Tile* PictureLayerImpl::LayerRasterTileIterator::operator*() const {
+  DCHECK(*this);
 
-  // Higher resolution non-ideal goes first.
-  for (size_t i = 0; i < high_res_tiling_index; ++i) {
-    iterators_.push_back(PictureLayerTiling::TilingEvictionTileIterator(
-        layer_->tilings_->tiling_at(i), tree_priority));
-  }
+  IteratorType index = stages_[current_stage_].iterator_type;
+  DCHECK(iterators_[index]);
+  DCHECK(iterators_[index].get_type() == stages_[current_stage_].tile_type);
 
-  // Lower resolution non-ideal goes next.
-  for (size_t i = layer_->tilings_->num_tilings() - 1;
-       i > high_res_tiling_index;
-       --i) {
-    PictureLayerTiling* tiling = layer_->tilings_->tiling_at(i);
-    if (tiling->resolution() == LOW_RESOLUTION)
-      continue;
+  return *iterators_[index];
+}
 
-    iterators_.push_back(
-        PictureLayerTiling::TilingEvictionTileIterator(tiling, tree_priority));
-  }
+void PictureLayerImpl::LayerRasterTileIterator::AdvanceToNextStage() {
+  DCHECK_LT(current_stage_, arraysize(stages_));
+  ++current_stage_;
+  while (current_stage_ < arraysize(stages_)) {
+    IteratorType index = stages_[current_stage_].iterator_type;
+    TilePriority::PriorityBin tile_type = stages_[current_stage_].tile_type;
 
-  // Now, put the low res tiling if we have one.
-  if (low_res_tiling_index < layer_->tilings_->num_tilings()) {
-    iterators_.push_back(PictureLayerTiling::TilingEvictionTileIterator(
-        layer_->tilings_->tiling_at(low_res_tiling_index), tree_priority));
+    if (iterators_[index] && iterators_[index].get_type() == tile_type)
+      break;
+    ++current_stage_;
   }
+}
 
-  // Finally, put the high res tiling if we have one.
-  if (high_res_tiling_index < layer_->tilings_->num_tilings()) {
-    iterators_.push_back(PictureLayerTiling::TilingEvictionTileIterator(
-        layer_->tilings_->tiling_at(high_res_tiling_index), tree_priority));
-  }
+PictureLayerImpl::LayerEvictionTileIterator::LayerEvictionTileIterator()
+    : layer_(NULL),
+      tree_priority_(SAME_PRIORITY_FOR_BOTH_TREES),
+      current_category_(PictureLayerTiling::EVENTUALLY),
+      current_tiling_range_type_(PictureLayerTilingSet::HIGHER_THAN_HIGH_RES),
+      current_tiling_(0u) {
+}
 
-  DCHECK_GT(iterators_.size(), 0u);
+PictureLayerImpl::LayerEvictionTileIterator::LayerEvictionTileIterator(
+    PictureLayerImpl* layer,
+    TreePriority tree_priority)
+    : layer_(layer),
+      tree_priority_(tree_priority),
+      current_category_(PictureLayerTiling::EVENTUALLY),
+      current_tiling_range_type_(PictureLayerTilingSet::HIGHER_THAN_HIGH_RES),
+      current_tiling_(CurrentTilingRange().start - 1u) {
+  // TODO(vmpstr): Once tile priorities are determined by the iterators, ensure
+  // that layers that don't have valid tile priorities have lowest priorities so
+  // they evict their tiles first (crbug.com/381704)
+  DCHECK(layer_->tilings_);
+  do {
+    if (!AdvanceToNextTiling())
+      break;
 
-  if (!iterators_[iterator_index_] ||
-      !IsCorrectType(&iterators_[iterator_index_])) {
-    AdvanceToNextIterator();
-  }
+    current_iterator_ = PictureLayerTiling::TilingEvictionTileIterator(
+        layer_->tilings_->tiling_at(CurrentTilingIndex()),
+        tree_priority,
+        current_category_);
+  } while (!current_iterator_);
 }
 
-PictureLayerImpl::LayerEvictionTileIterator::~LayerEvictionTileIterator() {}
+PictureLayerImpl::LayerEvictionTileIterator::~LayerEvictionTileIterator() {
+}
 
 Tile* PictureLayerImpl::LayerEvictionTileIterator::operator*() {
   DCHECK(*this);
-  return *iterators_[iterator_index_];
+  return *current_iterator_;
+}
+
+const Tile* PictureLayerImpl::LayerEvictionTileIterator::operator*() const {
+  DCHECK(*this);
+  return *current_iterator_;
 }
 
 PictureLayerImpl::LayerEvictionTileIterator&
 PictureLayerImpl::LayerEvictionTileIterator::
 operator++() {
   DCHECK(*this);
-  ++iterators_[iterator_index_];
-  if (!iterators_[iterator_index_] ||
-      !IsCorrectType(&iterators_[iterator_index_])) {
-    AdvanceToNextIterator();
+  ++current_iterator_;
+  while (!current_iterator_) {
+    if (!AdvanceToNextTiling())
+      break;
+
+    current_iterator_ = PictureLayerTiling::TilingEvictionTileIterator(
+        layer_->tilings_->tiling_at(CurrentTilingIndex()),
+        tree_priority_,
+        current_category_);
   }
   return *this;
 }
 
-void PictureLayerImpl::LayerEvictionTileIterator::AdvanceToNextIterator() {
-  ++iterator_index_;
+PictureLayerImpl::LayerEvictionTileIterator::operator bool() const {
+  return !!current_iterator_;
+}
 
-  while (true) {
-    while (iterator_index_ < iterators_.size()) {
-      if (iterators_[iterator_index_] &&
-          IsCorrectType(&iterators_[iterator_index_])) {
-        return;
-      }
-      ++iterator_index_;
-    }
+bool PictureLayerImpl::LayerEvictionTileIterator::AdvanceToNextCategory() {
+  switch (current_category_) {
+    case PictureLayerTiling::EVENTUALLY:
+      current_category_ =
+          PictureLayerTiling::EVENTUALLY_AND_REQUIRED_FOR_ACTIVATION;
+      return true;
+    case PictureLayerTiling::EVENTUALLY_AND_REQUIRED_FOR_ACTIVATION:
+      current_category_ = PictureLayerTiling::SOON;
+      return true;
+    case PictureLayerTiling::SOON:
+      current_category_ = PictureLayerTiling::SOON_AND_REQUIRED_FOR_ACTIVATION;
+      return true;
+    case PictureLayerTiling::SOON_AND_REQUIRED_FOR_ACTIVATION:
+      current_category_ = PictureLayerTiling::NOW;
+      return true;
+    case PictureLayerTiling::NOW:
+      current_category_ = PictureLayerTiling::NOW_AND_REQUIRED_FOR_ACTIVATION;
+      return true;
+    case PictureLayerTiling::NOW_AND_REQUIRED_FOR_ACTIVATION:
+      return false;
+  }
+  NOTREACHED();
+  return false;
+}
 
-    // If we're NOW and required_for_activation, then this was the last pass
-    // through the iterators.
-    if (iteration_stage_ == TilePriority::NOW && required_for_activation_)
-      break;
+bool
+PictureLayerImpl::LayerEvictionTileIterator::AdvanceToNextTilingRangeType() {
+  switch (current_tiling_range_type_) {
+    case PictureLayerTilingSet::HIGHER_THAN_HIGH_RES:
+      current_tiling_range_type_ = PictureLayerTilingSet::LOWER_THAN_LOW_RES;
+      return true;
+    case PictureLayerTilingSet::LOWER_THAN_LOW_RES:
+      current_tiling_range_type_ =
+          PictureLayerTilingSet::BETWEEN_HIGH_AND_LOW_RES;
+      return true;
+    case PictureLayerTilingSet::BETWEEN_HIGH_AND_LOW_RES:
+      current_tiling_range_type_ = PictureLayerTilingSet::LOW_RES;
+      return true;
+    case PictureLayerTilingSet::LOW_RES:
+      current_tiling_range_type_ = PictureLayerTilingSet::HIGH_RES;
+      return true;
+    case PictureLayerTilingSet::HIGH_RES:
+      if (!AdvanceToNextCategory())
+        return false;
 
-    if (!required_for_activation_) {
-      required_for_activation_ = true;
-    } else {
-      required_for_activation_ = false;
-      iteration_stage_ =
-          static_cast<TilePriority::PriorityBin>(iteration_stage_ - 1);
-    }
-    iterator_index_ = 0;
+      current_tiling_range_type_ = PictureLayerTilingSet::HIGHER_THAN_HIGH_RES;
+      return true;
   }
+  NOTREACHED();
+  return false;
 }
 
-PictureLayerImpl::LayerEvictionTileIterator::operator bool() const {
-  return iterator_index_ < iterators_.size();
+bool PictureLayerImpl::LayerEvictionTileIterator::AdvanceToNextTiling() {
+  DCHECK_NE(current_tiling_, CurrentTilingRange().end);
+  ++current_tiling_;
+  while (current_tiling_ == CurrentTilingRange().end) {
+    if (!AdvanceToNextTilingRangeType())
+      return false;
+
+    current_tiling_ = CurrentTilingRange().start;
+  }
+  return true;
+}
+
+PictureLayerTilingSet::TilingRange
+PictureLayerImpl::LayerEvictionTileIterator::CurrentTilingRange() const {
+  return layer_->tilings_->GetTilingRange(current_tiling_range_type_);
 }
 
-bool PictureLayerImpl::LayerEvictionTileIterator::IsCorrectType(
-    PictureLayerTiling::TilingEvictionTileIterator* it) const {
-  return it->get_type() == iteration_stage_ &&
-         (**it)->required_for_activation() == required_for_activation_;
+size_t PictureLayerImpl::LayerEvictionTileIterator::CurrentTilingIndex() const {
+  DCHECK_NE(current_tiling_, CurrentTilingRange().end);
+  switch (current_tiling_range_type_) {
+    case PictureLayerTilingSet::HIGHER_THAN_HIGH_RES:
+    case PictureLayerTilingSet::LOW_RES:
+    case PictureLayerTilingSet::HIGH_RES:
+      return current_tiling_;
+    // Tilings in the following ranges are accessed in reverse order.
+    case PictureLayerTilingSet::BETWEEN_HIGH_AND_LOW_RES:
+    case PictureLayerTilingSet::LOWER_THAN_LOW_RES: {
+      PictureLayerTilingSet::TilingRange tiling_range = CurrentTilingRange();
+      size_t current_tiling_range_offset = current_tiling_ - tiling_range.start;
+      return tiling_range.end - 1 - current_tiling_range_offset;
+    }
+  }
+  NOTREACHED();
+  return 0;
 }
 
 }  // namespace cc