| Index: cc/resources/picture_layer_tiling.cc
 | 
| diff --git a/cc/resources/picture_layer_tiling.cc b/cc/resources/picture_layer_tiling.cc
 | 
| index e9e3b86180a0dba8a297dc77235f6f84224fe191..d2d54846ad2428685146648203bd60876c1b84db 100644
 | 
| --- a/cc/resources/picture_layer_tiling.cc
 | 
| +++ b/cc/resources/picture_layer_tiling.cc
 | 
| @@ -25,63 +25,44 @@ namespace {
 | 
|  
 | 
|  const float kSoonBorderDistanceInScreenPixels = 312.f;
 | 
|  
 | 
| -class TileEvictionOrder {
 | 
| - public:
 | 
| -  explicit TileEvictionOrder(TreePriority tree_priority)
 | 
| -      : tree_priority_(tree_priority) {}
 | 
| -  ~TileEvictionOrder() {}
 | 
| -
 | 
| -  bool operator()(const Tile* a, const Tile* b) {
 | 
| -    const TilePriority& a_priority =
 | 
| -        a->priority_for_tree_priority(tree_priority_);
 | 
| -    const TilePriority& b_priority =
 | 
| -        b->priority_for_tree_priority(tree_priority_);
 | 
| -
 | 
| -    DCHECK(a_priority.priority_bin == b_priority.priority_bin);
 | 
| -    DCHECK(a->required_for_activation() == b->required_for_activation());
 | 
| -
 | 
| -    // Or if a is occluded and b is unoccluded.
 | 
| -    bool a_is_occluded = a->is_occluded_for_tree_priority(tree_priority_);
 | 
| -    bool b_is_occluded = b->is_occluded_for_tree_priority(tree_priority_);
 | 
| -    if (a_is_occluded != b_is_occluded)
 | 
| -      return a_is_occluded;
 | 
| -
 | 
| -    // Or if a is farther away from visible.
 | 
| -    return a_priority.distance_to_visible > b_priority.distance_to_visible;
 | 
| -  }
 | 
| -
 | 
| - private:
 | 
| -  TreePriority tree_priority_;
 | 
| -};
 | 
| -
 | 
|  }  // namespace
 | 
|  
 | 
|  scoped_ptr<PictureLayerTiling> PictureLayerTiling::Create(
 | 
|      float contents_scale,
 | 
|      const gfx::Size& layer_bounds,
 | 
| -    PictureLayerTilingClient* client) {
 | 
| -  return make_scoped_ptr(new PictureLayerTiling(contents_scale,
 | 
| -                                                layer_bounds,
 | 
| -                                                client));
 | 
| +    PictureLayerTilingClient* client,
 | 
| +    size_t max_tiles_for_interest_area,
 | 
| +    float skewport_target_time_in_seconds,
 | 
| +    int skewport_extrapolation_limit_in_content_pixels) {
 | 
| +  return make_scoped_ptr(new PictureLayerTiling(
 | 
| +      contents_scale, layer_bounds, client, max_tiles_for_interest_area,
 | 
| +      skewport_target_time_in_seconds,
 | 
| +      skewport_extrapolation_limit_in_content_pixels));
 | 
|  }
 | 
|  
 | 
| -PictureLayerTiling::PictureLayerTiling(float contents_scale,
 | 
| -                                       const gfx::Size& layer_bounds,
 | 
| -                                       PictureLayerTilingClient* client)
 | 
| -    : contents_scale_(contents_scale),
 | 
| +PictureLayerTiling::PictureLayerTiling(
 | 
| +    float contents_scale,
 | 
| +    const gfx::Size& layer_bounds,
 | 
| +    PictureLayerTilingClient* client,
 | 
| +    size_t max_tiles_for_interest_area,
 | 
| +    float skewport_target_time_in_seconds,
 | 
| +    int skewport_extrapolation_limit_in_content_pixels)
 | 
| +    : max_tiles_for_interest_area_(max_tiles_for_interest_area),
 | 
| +      skewport_target_time_in_seconds_(skewport_target_time_in_seconds),
 | 
| +      skewport_extrapolation_limit_in_content_pixels_(
 | 
| +          skewport_extrapolation_limit_in_content_pixels),
 | 
| +      contents_scale_(contents_scale),
 | 
|        layer_bounds_(layer_bounds),
 | 
|        resolution_(NON_IDEAL_RESOLUTION),
 | 
|        client_(client),
 | 
|        tiling_data_(gfx::Size(), gfx::Size(), kBorderTexels),
 | 
|        last_impl_frame_time_in_seconds_(0.0),
 | 
| -      content_to_screen_scale_(0.f),
 | 
|        can_require_tiles_for_activation_(false),
 | 
| +      current_content_to_screen_scale_(0.f),
 | 
|        has_visible_rect_tiles_(false),
 | 
|        has_skewport_rect_tiles_(false),
 | 
|        has_soon_border_rect_tiles_(false),
 | 
| -      has_eventually_rect_tiles_(false),
 | 
| -      eviction_tiles_cache_valid_(false),
 | 
| -      eviction_cache_tree_priority_(SAME_PRIORITY_FOR_BOTH_TREES) {
 | 
| +      has_eventually_rect_tiles_(false) {
 | 
|    gfx::Size content_bounds =
 | 
|        gfx::ToCeiledSize(gfx::ScaleSize(layer_bounds, contents_scale));
 | 
|    gfx::Size tile_size = client_->CalculateTileSize(content_bounds);
 | 
| @@ -105,13 +86,13 @@ PictureLayerTiling::~PictureLayerTiling() {
 | 
|      it->second->set_shared(false);
 | 
|  }
 | 
|  
 | 
| -void PictureLayerTiling::SetClient(PictureLayerTilingClient* client) {
 | 
| -  client_ = client;
 | 
| -}
 | 
| -
 | 
|  Tile* PictureLayerTiling::CreateTile(int i,
 | 
|                                       int j,
 | 
| -                                     const PictureLayerTiling* twin_tiling) {
 | 
| +                                     const PictureLayerTiling* twin_tiling,
 | 
| +                                     PictureLayerTiling* recycled_twin) {
 | 
| +  // Can't have both a (pending or active) twin and a recycled twin tiling.
 | 
| +  DCHECK_IMPLIES(twin_tiling, !recycled_twin);
 | 
| +  DCHECK_IMPLIES(recycled_twin, !twin_tiling);
 | 
|    TileMapKey key(i, j);
 | 
|    DCHECK(tiles_.find(key) == tiles_.end());
 | 
|  
 | 
| @@ -144,14 +125,24 @@ Tile* PictureLayerTiling::CreateTile(int i,
 | 
|      DCHECK(!tile->is_shared());
 | 
|      tile->set_tiling_index(i, j);
 | 
|      tiles_[key] = tile;
 | 
| +
 | 
| +    if (recycled_twin) {
 | 
| +      DCHECK(recycled_twin->tiles_.find(key) == recycled_twin->tiles_.end());
 | 
| +      // Do what recycled_twin->CreateTile() would do.
 | 
| +      tile->set_shared(true);
 | 
| +      recycled_twin->tiles_[key] = tile;
 | 
| +    }
 | 
|    }
 | 
| -  eviction_tiles_cache_valid_ = false;
 | 
|    return tile.get();
 | 
|  }
 | 
|  
 | 
|  void PictureLayerTiling::CreateMissingTilesInLiveTilesRect() {
 | 
|    const PictureLayerTiling* twin_tiling =
 | 
|        client_->GetPendingOrActiveTwinTiling(this);
 | 
| +  // There is no recycled twin during commit from the main thread which is when
 | 
| +  // this occurs.
 | 
| +  PictureLayerTiling* null_recycled_twin = nullptr;
 | 
| +  DCHECK_EQ(null_recycled_twin, client_->GetRecycledTwinTiling(this));
 | 
|    bool include_borders = false;
 | 
|    for (TilingData::Iterator iter(
 | 
|             &tiling_data_, live_tiles_rect_, include_borders);
 | 
| @@ -161,116 +152,160 @@ void PictureLayerTiling::CreateMissingTilesInLiveTilesRect() {
 | 
|      TileMap::iterator find = tiles_.find(key);
 | 
|      if (find != tiles_.end())
 | 
|        continue;
 | 
| -    CreateTile(key.first, key.second, twin_tiling);
 | 
| +    CreateTile(key.first, key.second, twin_tiling, null_recycled_twin);
 | 
|    }
 | 
|  
 | 
| -  VerifyLiveTilesRect();
 | 
| +  VerifyLiveTilesRect(false);
 | 
|  }
 | 
|  
 | 
| -void PictureLayerTiling::UpdateTilesToCurrentRasterSource(
 | 
| -    RasterSource* raster_source,
 | 
| -    const Region& layer_invalidation,
 | 
| -    const gfx::Size& new_layer_bounds) {
 | 
| -  DCHECK(!new_layer_bounds.IsEmpty());
 | 
| +void PictureLayerTiling::CloneTilesAndPropertiesFrom(
 | 
| +    const PictureLayerTiling& twin_tiling) {
 | 
| +  DCHECK_EQ(&twin_tiling, client_->GetPendingOrActiveTwinTiling(this));
 | 
|  
 | 
| -  gfx::Size content_bounds =
 | 
| -      gfx::ToCeiledSize(gfx::ScaleSize(new_layer_bounds, contents_scale_));
 | 
| -  gfx::Size tile_size = client_->CalculateTileSize(content_bounds);
 | 
| +  Resize(twin_tiling.layer_bounds_);
 | 
| +  DCHECK_EQ(twin_tiling.contents_scale_, contents_scale_);
 | 
| +  DCHECK_EQ(twin_tiling.layer_bounds().ToString(), layer_bounds().ToString());
 | 
| +  DCHECK_EQ(twin_tiling.tile_size().ToString(), tile_size().ToString());
 | 
|  
 | 
| -  if (new_layer_bounds != layer_bounds_) {
 | 
| -    if (tile_size.IsEmpty()) {
 | 
| -      layer_bounds_ = gfx::Size();
 | 
| -      content_bounds = gfx::Size();
 | 
| -    } else {
 | 
| -      layer_bounds_ = new_layer_bounds;
 | 
| -    }
 | 
| +  resolution_ = twin_tiling.resolution_;
 | 
|  
 | 
| -    // The SetLiveTilesRect() method would drop tiles outside the new bounds,
 | 
| -    // but may do so incorrectly if resizing the tiling causes the number of
 | 
| -    // tiles in the tiling_data_ to change.
 | 
| -    gfx::Rect content_rect(content_bounds);
 | 
| -    int before_left = tiling_data_.TileXIndexFromSrcCoord(live_tiles_rect_.x());
 | 
| -    int before_top = tiling_data_.TileYIndexFromSrcCoord(live_tiles_rect_.y());
 | 
| -    int before_right =
 | 
| -        tiling_data_.TileXIndexFromSrcCoord(live_tiles_rect_.right() - 1);
 | 
| -    int before_bottom =
 | 
| -        tiling_data_.TileYIndexFromSrcCoord(live_tiles_rect_.bottom() - 1);
 | 
| +  SetLiveTilesRect(twin_tiling.live_tiles_rect());
 | 
|  
 | 
| -    // The live_tiles_rect_ is clamped to stay within the tiling size as we
 | 
| -    // change it.
 | 
| -    live_tiles_rect_.Intersect(content_rect);
 | 
| -    tiling_data_.SetTilingSize(content_bounds);
 | 
| +  // Recreate unshared tiles.
 | 
| +  std::vector<TileMapKey> to_remove;
 | 
| +  for (const auto& tile_map_pair : tiles_) {
 | 
| +    TileMapKey key = tile_map_pair.first;
 | 
| +    Tile* tile = tile_map_pair.second.get();
 | 
| +    if (!tile->is_shared())
 | 
| +      to_remove.push_back(key);
 | 
| +  }
 | 
| +  // The recycled twin does not exist since there is a pending twin (which is
 | 
| +  // |twin_tiling|).
 | 
| +  PictureLayerTiling* null_recycled_twin = nullptr;
 | 
| +  DCHECK_EQ(null_recycled_twin, client_->GetRecycledTwinTiling(this));
 | 
| +  for (const auto& key : to_remove) {
 | 
| +    RemoveTileAt(key.first, key.second, null_recycled_twin);
 | 
| +    CreateTile(key.first, key.second, &twin_tiling, null_recycled_twin);
 | 
| +  }
 | 
|  
 | 
| -    int after_right = -1;
 | 
| -    int after_bottom = -1;
 | 
| -    if (!live_tiles_rect_.IsEmpty()) {
 | 
| -      after_right =
 | 
| -          tiling_data_.TileXIndexFromSrcCoord(live_tiles_rect_.right() - 1);
 | 
| -      after_bottom =
 | 
| -          tiling_data_.TileYIndexFromSrcCoord(live_tiles_rect_.bottom() - 1);
 | 
| -    }
 | 
| +  // Create any missing tiles from the |twin_tiling|.
 | 
| +  for (const auto& tile_map_pair : twin_tiling.tiles_) {
 | 
| +    TileMapKey key = tile_map_pair.first;
 | 
| +    Tile* tile = tile_map_pair.second.get();
 | 
| +    if (!tile->is_shared())
 | 
| +      CreateTile(key.first, key.second, &twin_tiling, null_recycled_twin);
 | 
| +  }
 | 
|  
 | 
| -    // There is no recycled twin since this is run on the pending tiling.
 | 
| -    PictureLayerTiling* recycled_twin = NULL;
 | 
| -    DCHECK_EQ(recycled_twin, client_->GetRecycledTwinTiling(this));
 | 
| -    DCHECK_EQ(PENDING_TREE, client_->GetTree());
 | 
| +  DCHECK_EQ(twin_tiling.tiles_.size(), tiles_.size());
 | 
| +#if DCHECK_IS_ON
 | 
| +  for (const auto& tile_map_pair : tiles_)
 | 
| +    DCHECK(tile_map_pair.second->is_shared());
 | 
| +  VerifyLiveTilesRect(false);
 | 
| +#endif
 | 
|  
 | 
| -    // Drop tiles outside the new layer bounds if the layer shrank.
 | 
| -    for (int i = after_right + 1; i <= before_right; ++i) {
 | 
| -      for (int j = before_top; j <= before_bottom; ++j)
 | 
| -        RemoveTileAt(i, j, recycled_twin);
 | 
| -    }
 | 
| -    for (int i = before_left; i <= after_right; ++i) {
 | 
| -      for (int j = after_bottom + 1; j <= before_bottom; ++j)
 | 
| -        RemoveTileAt(i, j, recycled_twin);
 | 
| -    }
 | 
| +  UpdateTilePriorityRects(twin_tiling.current_content_to_screen_scale_,
 | 
| +                          twin_tiling.current_visible_rect_,
 | 
| +                          twin_tiling.current_skewport_rect_,
 | 
| +                          twin_tiling.current_soon_border_rect_,
 | 
| +                          twin_tiling.current_eventually_rect_,
 | 
| +                          twin_tiling.current_occlusion_in_layer_space_);
 | 
| +}
 | 
|  
 | 
| -    // If the layer grew, the live_tiles_rect_ is not changed, but a new row
 | 
| -    // and/or column of tiles may now exist inside the same live_tiles_rect_.
 | 
| -    const PictureLayerTiling* twin_tiling =
 | 
| -        client_->GetPendingOrActiveTwinTiling(this);
 | 
| -    if (after_right > before_right) {
 | 
| -      DCHECK_EQ(after_right, before_right + 1);
 | 
| -      for (int j = before_top; j <= after_bottom; ++j)
 | 
| -        CreateTile(after_right, j, twin_tiling);
 | 
| -    }
 | 
| -    if (after_bottom > before_bottom) {
 | 
| -      DCHECK_EQ(after_bottom, before_bottom + 1);
 | 
| -      for (int i = before_left; i <= before_right; ++i)
 | 
| -        CreateTile(i, after_bottom, twin_tiling);
 | 
| -    }
 | 
| +void PictureLayerTiling::Resize(const gfx::Size& new_layer_bounds) {
 | 
| +  gfx::Size layer_bounds = new_layer_bounds;
 | 
| +  gfx::Size content_bounds =
 | 
| +      gfx::ToCeiledSize(gfx::ScaleSize(new_layer_bounds, contents_scale_));
 | 
| +  gfx::Size tile_size = client_->CalculateTileSize(content_bounds);
 | 
| +
 | 
| +  if (tile_size.IsEmpty()) {
 | 
| +    layer_bounds = gfx::Size();
 | 
| +    content_bounds = gfx::Size();
 | 
|    }
 | 
|  
 | 
| +  // The layer bounds are only allowed to be empty when the tile size is empty.
 | 
| +  // Otherwise we should not have such a tiling in the first place.
 | 
| +  DCHECK_IMPLIES(!tile_size.IsEmpty(), !layer_bounds_.IsEmpty());
 | 
| +
 | 
| +  bool resized = layer_bounds != layer_bounds_;
 | 
| +  layer_bounds_ = layer_bounds;
 | 
| +
 | 
|    if (tile_size != tiling_data_.max_texture_size()) {
 | 
| +    tiling_data_.SetTilingSize(content_bounds);
 | 
|      tiling_data_.SetMaxTextureSize(tile_size);
 | 
|      // When the tile size changes, the TilingData positions no longer work
 | 
| -    // as valid keys to the TileMap, so just drop all tiles.
 | 
| +    // as valid keys to the TileMap, so just drop all tiles and clear the live
 | 
| +    // tiles rect.
 | 
|      Reset();
 | 
| -  } else {
 | 
| -    Invalidate(layer_invalidation);
 | 
| +    return;
 | 
|    }
 | 
|  
 | 
| -  for (TileMap::const_iterator it = tiles_.begin(); it != tiles_.end(); ++it)
 | 
| -    it->second->set_raster_source(raster_source);
 | 
| -  VerifyLiveTilesRect();
 | 
| -}
 | 
| +  if (!resized)
 | 
| +    return;
 | 
|  
 | 
| -void PictureLayerTiling::RemoveTilesInRegion(const Region& layer_region) {
 | 
| -  bool recreate_invalidated_tiles = false;
 | 
| -  DoInvalidate(layer_region, recreate_invalidated_tiles);
 | 
| -}
 | 
| +  // The SetLiveTilesRect() method would drop tiles outside the new bounds,
 | 
| +  // but may do so incorrectly if resizing the tiling causes the number of
 | 
| +  // tiles in the tiling_data_ to change.
 | 
| +  gfx::Rect content_rect(content_bounds);
 | 
| +  int before_left = tiling_data_.TileXIndexFromSrcCoord(live_tiles_rect_.x());
 | 
| +  int before_top = tiling_data_.TileYIndexFromSrcCoord(live_tiles_rect_.y());
 | 
| +  int before_right =
 | 
| +      tiling_data_.TileXIndexFromSrcCoord(live_tiles_rect_.right() - 1);
 | 
| +  int before_bottom =
 | 
| +      tiling_data_.TileYIndexFromSrcCoord(live_tiles_rect_.bottom() - 1);
 | 
| +
 | 
| +  // The live_tiles_rect_ is clamped to stay within the tiling size as we
 | 
| +  // change it.
 | 
| +  live_tiles_rect_.Intersect(content_rect);
 | 
| +  tiling_data_.SetTilingSize(content_bounds);
 | 
| +
 | 
| +  int after_right = -1;
 | 
| +  int after_bottom = -1;
 | 
| +  if (!live_tiles_rect_.IsEmpty()) {
 | 
| +    after_right =
 | 
| +        tiling_data_.TileXIndexFromSrcCoord(live_tiles_rect_.right() - 1);
 | 
| +    after_bottom =
 | 
| +        tiling_data_.TileYIndexFromSrcCoord(live_tiles_rect_.bottom() - 1);
 | 
| +  }
 | 
| +
 | 
| +  // There is no recycled twin since this is run on the pending tiling
 | 
| +  // during commit, and on the active tree during activate.
 | 
| +  PictureLayerTiling* null_recycled_twin = nullptr;
 | 
| +  DCHECK_EQ(null_recycled_twin, client_->GetRecycledTwinTiling(this));
 | 
|  
 | 
| -void PictureLayerTiling::Invalidate(const Region& layer_region) {
 | 
| -  bool recreate_invalidated_tiles = true;
 | 
| -  DoInvalidate(layer_region, recreate_invalidated_tiles);
 | 
| +  // Drop tiles outside the new layer bounds if the layer shrank.
 | 
| +  for (int i = after_right + 1; i <= before_right; ++i) {
 | 
| +    for (int j = before_top; j <= before_bottom; ++j)
 | 
| +      RemoveTileAt(i, j, null_recycled_twin);
 | 
| +  }
 | 
| +  for (int i = before_left; i <= after_right; ++i) {
 | 
| +    for (int j = after_bottom + 1; j <= before_bottom; ++j)
 | 
| +      RemoveTileAt(i, j, null_recycled_twin);
 | 
| +  }
 | 
| +
 | 
| +  // If the layer grew, the live_tiles_rect_ is not changed, but a new row
 | 
| +  // and/or column of tiles may now exist inside the same live_tiles_rect_.
 | 
| +  const PictureLayerTiling* twin_tiling =
 | 
| +      client_->GetPendingOrActiveTwinTiling(this);
 | 
| +  if (after_right > before_right) {
 | 
| +    DCHECK_EQ(after_right, before_right + 1);
 | 
| +    for (int j = before_top; j <= after_bottom; ++j)
 | 
| +      CreateTile(after_right, j, twin_tiling, null_recycled_twin);
 | 
| +  }
 | 
| +  if (after_bottom > before_bottom) {
 | 
| +    DCHECK_EQ(after_bottom, before_bottom + 1);
 | 
| +    for (int i = before_left; i <= before_right; ++i)
 | 
| +      CreateTile(i, after_bottom, twin_tiling, null_recycled_twin);
 | 
| +  }
 | 
|  }
 | 
|  
 | 
| -void PictureLayerTiling::DoInvalidate(const Region& layer_region,
 | 
| -                                      bool recreate_invalidated_tiles) {
 | 
| +void PictureLayerTiling::Invalidate(const Region& layer_invalidation) {
 | 
| +  if (live_tiles_rect_.IsEmpty())
 | 
| +    return;
 | 
|    std::vector<TileMapKey> new_tile_keys;
 | 
|    gfx::Rect expanded_live_tiles_rect =
 | 
|        tiling_data_.ExpandRectIgnoringBordersToTileBounds(live_tiles_rect_);
 | 
| -  for (Region::Iterator iter(layer_region); iter.has_rect(); iter.next()) {
 | 
| +  for (Region::Iterator iter(layer_invalidation); iter.has_rect();
 | 
| +       iter.next()) {
 | 
|      gfx::Rect layer_rect = iter.rect();
 | 
|      gfx::Rect content_rect =
 | 
|          gfx::ScaleToEnclosingRect(layer_rect, contents_scale_);
 | 
| @@ -291,25 +326,40 @@ void PictureLayerTiling::DoInvalidate(const Region& layer_region,
 | 
|               &tiling_data_, content_rect, include_borders);
 | 
|           iter;
 | 
|           ++iter) {
 | 
| -      // There is no recycled twin since this is run on the pending tiling.
 | 
| -      PictureLayerTiling* recycled_twin = NULL;
 | 
| -      DCHECK_EQ(recycled_twin, client_->GetRecycledTwinTiling(this));
 | 
| -      DCHECK_EQ(PENDING_TREE, client_->GetTree());
 | 
| -      if (RemoveTileAt(iter.index_x(), iter.index_y(), recycled_twin))
 | 
| +      // There is no recycled twin for the pending tree during commit, or for
 | 
| +      // the active tree during activation.
 | 
| +      PictureLayerTiling* null_recycled_twin = nullptr;
 | 
| +      DCHECK_EQ(null_recycled_twin, client_->GetRecycledTwinTiling(this));
 | 
| +      if (RemoveTileAt(iter.index_x(), iter.index_y(), null_recycled_twin))
 | 
|          new_tile_keys.push_back(iter.index());
 | 
|      }
 | 
|    }
 | 
|  
 | 
| -  if (recreate_invalidated_tiles && !new_tile_keys.empty()) {
 | 
| +  if (!new_tile_keys.empty()) {
 | 
| +    // During commit from the main thread, invalidations can never be shared
 | 
| +    // with the active tree since the active tree has different content there.
 | 
| +    // And when invalidating an active-tree tiling, it means there was no
 | 
| +    // pending tiling to clone from.
 | 
| +    const PictureLayerTiling* null_twin_tiling = nullptr;
 | 
| +    PictureLayerTiling* null_recycled_twin = nullptr;
 | 
| +    DCHECK_EQ(null_recycled_twin, client_->GetRecycledTwinTiling(this));
 | 
|      for (size_t i = 0; i < new_tile_keys.size(); ++i) {
 | 
| -      // Don't try to share a tile with the twin layer, it's been invalidated so
 | 
| -      // we have to make our own tile here.
 | 
| -      const PictureLayerTiling* twin_tiling = NULL;
 | 
| -      CreateTile(new_tile_keys[i].first, new_tile_keys[i].second, twin_tiling);
 | 
| +      CreateTile(new_tile_keys[i].first, new_tile_keys[i].second,
 | 
| +                 null_twin_tiling, null_recycled_twin);
 | 
|      }
 | 
|    }
 | 
|  }
 | 
|  
 | 
| +void PictureLayerTiling::SetRasterSource(
 | 
| +    scoped_refptr<RasterSource> raster_source) {
 | 
| +  // Shared (ie. non-invalidated) tiles on the pending tree are updated to use
 | 
| +  // the new raster source. When this raster source is activated, the raster
 | 
| +  // source will remain valid for shared tiles in the active tree.
 | 
| +  for (TileMap::const_iterator it = tiles_.begin(); it != tiles_.end(); ++it)
 | 
| +    it->second->set_raster_source(raster_source);
 | 
| +  VerifyLiveTilesRect(false);
 | 
| +}
 | 
| +
 | 
|  PictureLayerTiling::CoverageIterator::CoverageIterator()
 | 
|      : tiling_(NULL),
 | 
|        current_tile_(NULL),
 | 
| @@ -468,10 +518,9 @@ bool PictureLayerTiling::RemoveTileAt(int i,
 | 
|      return false;
 | 
|    found->second->set_shared(false);
 | 
|    tiles_.erase(found);
 | 
| -  eviction_tiles_cache_valid_ = false;
 | 
|    if (recycled_twin) {
 | 
| -    // Recycled twin does not also have a recycled twin, so pass NULL.
 | 
| -    recycled_twin->RemoveTileAt(i, j, NULL);
 | 
| +    // Recycled twin does not also have a recycled twin, so pass null.
 | 
| +    recycled_twin->RemoveTileAt(i, j, nullptr);
 | 
|    }
 | 
|    return true;
 | 
|  }
 | 
| @@ -482,10 +531,9 @@ void PictureLayerTiling::Reset() {
 | 
|    for (TileMap::const_iterator it = tiles_.begin(); it != tiles_.end(); ++it) {
 | 
|      it->second->set_shared(false);
 | 
|      if (recycled_twin)
 | 
| -      recycled_twin->RemoveTileAt(it->first.first, it->first.second, NULL);
 | 
| +      recycled_twin->RemoveTileAt(it->first.first, it->first.second, nullptr);
 | 
|    }
 | 
|    tiles_.clear();
 | 
| -  eviction_tiles_cache_valid_ = false;
 | 
|  }
 | 
|  
 | 
|  gfx::Rect PictureLayerTiling::ComputeSkewport(
 | 
| @@ -500,10 +548,8 @@ gfx::Rect PictureLayerTiling::ComputeSkewport(
 | 
|    if (time_delta == 0.0)
 | 
|      return skewport;
 | 
|  
 | 
| -  float skewport_target_time_in_seconds =
 | 
| -      client_->GetSkewportTargetTimeInSeconds();
 | 
|    double extrapolation_multiplier =
 | 
| -      skewport_target_time_in_seconds / time_delta;
 | 
| +      skewport_target_time_in_seconds_ / time_delta;
 | 
|  
 | 
|    int old_x = last_visible_rect_in_content_space_.x();
 | 
|    int old_y = last_visible_rect_in_content_space_.y();
 | 
| @@ -515,12 +561,11 @@ gfx::Rect PictureLayerTiling::ComputeSkewport(
 | 
|    int new_right = visible_rect_in_content_space.right();
 | 
|    int new_bottom = visible_rect_in_content_space.bottom();
 | 
|  
 | 
| -  int skewport_limit = client_->GetSkewportExtrapolationLimitInContentPixels();
 | 
| -
 | 
| -  // Compute the maximum skewport based on |skewport_limit|.
 | 
| +  // Compute the maximum skewport based on
 | 
| +  // |skewport_extrapolation_limit_in_content_pixels_|.
 | 
|    gfx::Rect max_skewport = skewport;
 | 
| -  max_skewport.Inset(
 | 
| -      -skewport_limit, -skewport_limit, -skewport_limit, -skewport_limit);
 | 
| +  max_skewport.Inset(-skewport_extrapolation_limit_in_content_pixels_,
 | 
| +                     -skewport_extrapolation_limit_in_content_pixels_);
 | 
|  
 | 
|    // Inset the skewport by the needed adjustment.
 | 
|    skewport.Inset(extrapolation_multiplier * (new_x - old_x),
 | 
| @@ -564,11 +609,9 @@ void PictureLayerTiling::ComputeTilePriorityRects(
 | 
|    DCHECK(skewport.Contains(visible_rect_in_content_space));
 | 
|  
 | 
|    // Calculate the eventually/live tiles rect.
 | 
| -  size_t max_tiles_for_interest_area = client_->GetMaxTilesForInterestArea();
 | 
| -
 | 
|    gfx::Size tile_size = tiling_data_.max_texture_size();
 | 
|    int64 eventually_rect_area =
 | 
| -      max_tiles_for_interest_area * tile_size.width() * tile_size.height();
 | 
| +      max_tiles_for_interest_area_ * tile_size.width() * tile_size.height();
 | 
|  
 | 
|    gfx::Rect eventually_rect =
 | 
|        ExpandRectEquallyToAreaBoundedBy(visible_rect_in_content_space,
 | 
| @@ -582,29 +625,36 @@ void PictureLayerTiling::ComputeTilePriorityRects(
 | 
|        << " eventually_rect: " << eventually_rect.ToString();
 | 
|  
 | 
|    // Calculate the soon border rect.
 | 
| -  content_to_screen_scale_ = ideal_contents_scale / contents_scale_;
 | 
| +  float content_to_screen_scale = ideal_contents_scale / contents_scale_;
 | 
|    gfx::Rect soon_border_rect = visible_rect_in_content_space;
 | 
| -  float border = kSoonBorderDistanceInScreenPixels / content_to_screen_scale_;
 | 
| +  float border = kSoonBorderDistanceInScreenPixels / content_to_screen_scale;
 | 
|    soon_border_rect.Inset(-border, -border, -border, -border);
 | 
|  
 | 
| -  // Update the tiling state.
 | 
| -  SetLiveTilesRect(eventually_rect);
 | 
| -
 | 
|    last_impl_frame_time_in_seconds_ = current_frame_time_in_seconds;
 | 
|    last_viewport_in_layer_space_ = viewport_in_layer_space;
 | 
|    last_visible_rect_in_content_space_ = visible_rect_in_content_space;
 | 
|  
 | 
| -  eviction_tiles_cache_valid_ = false;
 | 
| +  SetLiveTilesRect(eventually_rect);
 | 
| +  UpdateTilePriorityRects(
 | 
| +      content_to_screen_scale, visible_rect_in_content_space, skewport,
 | 
| +      soon_border_rect, eventually_rect, occlusion_in_layer_space);
 | 
| +}
 | 
|  
 | 
| +void PictureLayerTiling::UpdateTilePriorityRects(
 | 
| +    float content_to_screen_scale,
 | 
| +    const gfx::Rect& visible_rect_in_content_space,
 | 
| +    const gfx::Rect& skewport,
 | 
| +    const gfx::Rect& soon_border_rect,
 | 
| +    const gfx::Rect& eventually_rect,
 | 
| +    const Occlusion& occlusion_in_layer_space) {
 | 
|    current_visible_rect_ = visible_rect_in_content_space;
 | 
|    current_skewport_rect_ = skewport;
 | 
|    current_soon_border_rect_ = soon_border_rect;
 | 
|    current_eventually_rect_ = eventually_rect;
 | 
|    current_occlusion_in_layer_space_ = occlusion_in_layer_space;
 | 
| +  current_content_to_screen_scale_ = content_to_screen_scale;
 | 
|  
 | 
| -  // Update has_*_tiles state.
 | 
|    gfx::Rect tiling_rect(tiling_size());
 | 
| -
 | 
|    has_visible_rect_tiles_ = tiling_rect.Intersects(current_visible_rect_);
 | 
|    has_skewport_rect_tiles_ = tiling_rect.Intersects(current_skewport_rect_);
 | 
|    has_soon_border_rect_tiles_ =
 | 
| @@ -621,8 +671,9 @@ void PictureLayerTiling::SetLiveTilesRect(
 | 
|    if (live_tiles_rect_ == new_live_tiles_rect)
 | 
|      return;
 | 
|  
 | 
| -  // Iterate to delete all tiles outside of our new live_tiles rect.
 | 
|    PictureLayerTiling* recycled_twin = client_->GetRecycledTwinTiling(this);
 | 
| +
 | 
| +  // Iterate to delete all tiles outside of our new live_tiles rect.
 | 
|    for (TilingData::DifferenceIterator iter(&tiling_data_,
 | 
|                                             live_tiles_rect_,
 | 
|                                             new_live_tiles_rect);
 | 
| @@ -641,16 +692,20 @@ void PictureLayerTiling::SetLiveTilesRect(
 | 
|         iter;
 | 
|         ++iter) {
 | 
|      TileMapKey key(iter.index());
 | 
| -    CreateTile(key.first, key.second, twin_tiling);
 | 
| +    CreateTile(key.first, key.second, twin_tiling, recycled_twin);
 | 
|    }
 | 
|  
 | 
|    live_tiles_rect_ = new_live_tiles_rect;
 | 
| -  VerifyLiveTilesRect();
 | 
| +  VerifyLiveTilesRect(false);
 | 
| +  if (recycled_twin) {
 | 
| +    recycled_twin->live_tiles_rect_ = live_tiles_rect_;
 | 
| +    recycled_twin->VerifyLiveTilesRect(true);
 | 
| +  }
 | 
|  }
 | 
|  
 | 
| -void PictureLayerTiling::VerifyLiveTilesRect() {
 | 
| +void PictureLayerTiling::VerifyLiveTilesRect(bool is_on_recycle_tree) const {
 | 
|  #if DCHECK_IS_ON
 | 
| -  for (TileMap::iterator it = tiles_.begin(); it != tiles_.end(); ++it) {
 | 
| +  for (auto it = tiles_.begin(); it != tiles_.end(); ++it) {
 | 
|      if (!it->second.get())
 | 
|        continue;
 | 
|      DCHECK(it->first.first < tiling_data_.num_tiles_x())
 | 
| @@ -667,6 +722,7 @@ void PictureLayerTiling::VerifyLiveTilesRect() {
 | 
|          << " tile bounds "
 | 
|          << tiling_data_.TileBounds(it->first.first, it->first.second).ToString()
 | 
|          << " live_tiles_rect " << live_tiles_rect_.ToString();
 | 
| +    DCHECK_IMPLIES(is_on_recycle_tree, it->second->is_shared());
 | 
|    }
 | 
|  #endif
 | 
|  }
 | 
| @@ -805,10 +861,10 @@ void PictureLayerTiling::UpdateTilePriority(Tile* tile) const {
 | 
|      tile->set_required_for_draw(false);
 | 
|    tile->set_is_occluded(tree, false);
 | 
|  
 | 
| -  DCHECK_GT(content_to_screen_scale_, 0.f);
 | 
| +  DCHECK_GT(current_content_to_screen_scale_, 0.f);
 | 
|    float distance_to_visible =
 | 
|        current_visible_rect_.ManhattanInternalDistance(tile_bounds) *
 | 
| -      content_to_screen_scale_;
 | 
| +      current_content_to_screen_scale_;
 | 
|  
 | 
|    if (max_tile_priority_bin <= TilePriority::SOON &&
 | 
|        (current_soon_border_rect_.Intersects(tile_bounds) ||
 | 
| @@ -998,92 +1054,6 @@ gfx::Rect PictureLayerTiling::ExpandRectEquallyToAreaBoundedBy(
 | 
|    return result;
 | 
|  }
 | 
|  
 | 
| -void PictureLayerTiling::UpdateEvictionCacheIfNeeded(
 | 
| -    TreePriority tree_priority) {
 | 
| -  if (eviction_tiles_cache_valid_ &&
 | 
| -      eviction_cache_tree_priority_ == tree_priority)
 | 
| -    return;
 | 
| -
 | 
| -  eviction_tiles_now_.clear();
 | 
| -  eviction_tiles_now_and_required_for_activation_.clear();
 | 
| -  eviction_tiles_soon_.clear();
 | 
| -  eviction_tiles_soon_and_required_for_activation_.clear();
 | 
| -  eviction_tiles_eventually_.clear();
 | 
| -  eviction_tiles_eventually_and_required_for_activation_.clear();
 | 
| -
 | 
| -  for (TileMap::iterator it = tiles_.begin(); it != tiles_.end(); ++it) {
 | 
| -    Tile* tile = it->second.get();
 | 
| -    UpdateTileAndTwinPriority(tile);
 | 
| -    const TilePriority& priority =
 | 
| -        tile->priority_for_tree_priority(tree_priority);
 | 
| -    switch (priority.priority_bin) {
 | 
| -      case TilePriority::EVENTUALLY:
 | 
| -        if (tile->required_for_activation())
 | 
| -          eviction_tiles_eventually_and_required_for_activation_.push_back(
 | 
| -              tile);
 | 
| -        else
 | 
| -          eviction_tiles_eventually_.push_back(tile);
 | 
| -        break;
 | 
| -      case TilePriority::SOON:
 | 
| -        if (tile->required_for_activation())
 | 
| -          eviction_tiles_soon_and_required_for_activation_.push_back(tile);
 | 
| -        else
 | 
| -          eviction_tiles_soon_.push_back(tile);
 | 
| -        break;
 | 
| -      case TilePriority::NOW:
 | 
| -        if (tile->required_for_activation())
 | 
| -          eviction_tiles_now_and_required_for_activation_.push_back(tile);
 | 
| -        else
 | 
| -          eviction_tiles_now_.push_back(tile);
 | 
| -        break;
 | 
| -    }
 | 
| -  }
 | 
| -
 | 
| -  // TODO(vmpstr): Do this lazily. One option is to have a "sorted" flag that
 | 
| -  // can be updated for each of the queues.
 | 
| -  TileEvictionOrder sort_order(tree_priority);
 | 
| -  std::sort(eviction_tiles_now_.begin(), eviction_tiles_now_.end(), sort_order);
 | 
| -  std::sort(eviction_tiles_now_and_required_for_activation_.begin(),
 | 
| -            eviction_tiles_now_and_required_for_activation_.end(),
 | 
| -            sort_order);
 | 
| -  std::sort(
 | 
| -      eviction_tiles_soon_.begin(), eviction_tiles_soon_.end(), sort_order);
 | 
| -  std::sort(eviction_tiles_soon_and_required_for_activation_.begin(),
 | 
| -            eviction_tiles_soon_and_required_for_activation_.end(),
 | 
| -            sort_order);
 | 
| -  std::sort(eviction_tiles_eventually_.begin(),
 | 
| -            eviction_tiles_eventually_.end(),
 | 
| -            sort_order);
 | 
| -  std::sort(eviction_tiles_eventually_and_required_for_activation_.begin(),
 | 
| -            eviction_tiles_eventually_and_required_for_activation_.end(),
 | 
| -            sort_order);
 | 
| -
 | 
| -  eviction_tiles_cache_valid_ = true;
 | 
| -  eviction_cache_tree_priority_ = tree_priority;
 | 
| -}
 | 
| -
 | 
| -const std::vector<Tile*>* PictureLayerTiling::GetEvictionTiles(
 | 
| -    TreePriority tree_priority,
 | 
| -    EvictionCategory category) {
 | 
| -  UpdateEvictionCacheIfNeeded(tree_priority);
 | 
| -  switch (category) {
 | 
| -    case EVENTUALLY:
 | 
| -      return &eviction_tiles_eventually_;
 | 
| -    case EVENTUALLY_AND_REQUIRED_FOR_ACTIVATION:
 | 
| -      return &eviction_tiles_eventually_and_required_for_activation_;
 | 
| -    case SOON:
 | 
| -      return &eviction_tiles_soon_;
 | 
| -    case SOON_AND_REQUIRED_FOR_ACTIVATION:
 | 
| -      return &eviction_tiles_soon_and_required_for_activation_;
 | 
| -    case NOW:
 | 
| -      return &eviction_tiles_now_;
 | 
| -    case NOW_AND_REQUIRED_FOR_ACTIVATION:
 | 
| -      return &eviction_tiles_now_and_required_for_activation_;
 | 
| -  }
 | 
| -  NOTREACHED();
 | 
| -  return &eviction_tiles_eventually_;
 | 
| -}
 | 
| -
 | 
|  PictureLayerTiling::TilingRasterTileIterator::TilingRasterTileIterator()
 | 
|      : tiling_(NULL), current_tile_(NULL) {}
 | 
|  
 | 
| 
 |