Home | History | Annotate | Download | only in resources
      1 // Copyright 2012 The Chromium Authors. All rights reserved.
      2 // Use of this source code is governed by a BSD-style license that can be
      3 // found in the LICENSE file.
      4 
      5 #include "cc/resources/picture_pile.h"
      6 
      7 #include <algorithm>
      8 #include <limits>
      9 #include <vector>
     10 
     11 #include "cc/base/region.h"
     12 #include "cc/debug/rendering_stats_instrumentation.h"
     13 #include "cc/resources/picture_pile_impl.h"
     14 #include "cc/resources/raster_worker_pool.h"
     15 #include "cc/resources/tile_priority.h"
     16 
     17 namespace {
     18 // Layout pixel buffer around the visible layer rect to record.  Any base
     19 // picture that intersects the visible layer rect expanded by this distance
     20 // will be recorded.
     21 const int kPixelDistanceToRecord = 8000;
     22 // We don't perform solid color analysis on images that have more than 10 skia
     23 // operations.
     24 const int kOpCountThatIsOkToAnalyze = 10;
     25 
     26 // TODO(humper): The density threshold here is somewhat arbitrary; need a
     27 // way to set // this from the command line so we can write a benchmark
     28 // script and find a sweet spot.
     29 const float kDensityThreshold = 0.5f;
     30 
     31 bool rect_sort_y(const gfx::Rect& r1, const gfx::Rect& r2) {
     32   return r1.y() < r2.y() || (r1.y() == r2.y() && r1.x() < r2.x());
     33 }
     34 
     35 bool rect_sort_x(const gfx::Rect& r1, const gfx::Rect& r2) {
     36   return r1.x() < r2.x() || (r1.x() == r2.x() && r1.y() < r2.y());
     37 }
     38 
     39 float PerformClustering(const std::vector<gfx::Rect>& tiles,
     40                         std::vector<gfx::Rect>* clustered_rects) {
     41   // These variables track the record area and invalid area
     42   // for the entire clustering
     43   int total_record_area = 0;
     44   int total_invalid_area = 0;
     45 
     46   // These variables track the record area and invalid area
     47   // for the current cluster being constructed.
     48   gfx::Rect cur_record_rect;
     49   int cluster_record_area = 0, cluster_invalid_area = 0;
     50 
     51   for (std::vector<gfx::Rect>::const_iterator it = tiles.begin();
     52         it != tiles.end();
     53         it++) {
     54     gfx::Rect invalid_tile = *it;
     55 
     56     // For each tile, we consider adding the invalid tile to the
     57     // current record rectangle.  Only add it if the amount of empty
     58     // space created is below a density threshold.
     59     int tile_area = invalid_tile.width() * invalid_tile.height();
     60 
     61     gfx::Rect proposed_union = cur_record_rect;
     62     proposed_union.Union(invalid_tile);
     63     int proposed_area = proposed_union.width() * proposed_union.height();
     64     float proposed_density =
     65       static_cast<float>(cluster_invalid_area + tile_area) /
     66       static_cast<float>(proposed_area);
     67 
     68     if (proposed_density >= kDensityThreshold) {
     69       // It's okay to add this invalid tile to the
     70       // current recording rectangle.
     71       cur_record_rect = proposed_union;
     72       cluster_record_area = proposed_area;
     73       cluster_invalid_area += tile_area;
     74       total_invalid_area += tile_area;
     75     } else {
     76       // Adding this invalid tile to the current recording rectangle
     77       // would exceed our badness threshold, so put the current rectangle
     78       // in the list of recording rects, and start a new one.
     79       clustered_rects->push_back(cur_record_rect);
     80       total_record_area += cluster_record_area;
     81       cur_record_rect = invalid_tile;
     82       cluster_invalid_area = tile_area;
     83       cluster_record_area = tile_area;
     84     }
     85   }
     86 
     87   DCHECK(!cur_record_rect.IsEmpty());
     88   clustered_rects->push_back(cur_record_rect);
     89   total_record_area += cluster_record_area;;
     90 
     91   DCHECK_NE(total_record_area, 0);
     92 
     93   return static_cast<float>(total_invalid_area) /
     94          static_cast<float>(total_record_area);
     95 }
     96 
     97 float ClusterTiles(const std::vector<gfx::Rect>& invalid_tiles,
     98                    std::vector<gfx::Rect>* record_rects) {
     99   TRACE_EVENT1("cc", "ClusterTiles",
    100                "count",
    101                invalid_tiles.size());
    102 
    103   if (invalid_tiles.size() <= 1) {
    104     // Quickly handle the special case for common
    105     // single-invalidation update, and also the less common
    106     // case of no tiles passed in.
    107     *record_rects = invalid_tiles;
    108     return 1;
    109   }
    110 
    111   // Sort the invalid tiles by y coordinate.
    112   std::vector<gfx::Rect> invalid_tiles_vertical = invalid_tiles;
    113   std::sort(invalid_tiles_vertical.begin(),
    114             invalid_tiles_vertical.end(),
    115             rect_sort_y);
    116 
    117   float vertical_density;
    118   std::vector<gfx::Rect> vertical_clustering;
    119   vertical_density = PerformClustering(invalid_tiles_vertical,
    120                                        &vertical_clustering);
    121 
    122   // If vertical density is optimal, then we can return early.
    123   if (vertical_density == 1.f) {
    124     *record_rects = vertical_clustering;
    125     return vertical_density;
    126   }
    127 
    128   // Now try again with a horizontal sort, see which one is best
    129   std::vector<gfx::Rect> invalid_tiles_horizontal = invalid_tiles;
    130   std::sort(invalid_tiles_horizontal.begin(),
    131             invalid_tiles_horizontal.end(),
    132             rect_sort_x);
    133 
    134   float horizontal_density;
    135   std::vector<gfx::Rect> horizontal_clustering;
    136   horizontal_density = PerformClustering(invalid_tiles_horizontal,
    137                                          &horizontal_clustering);
    138 
    139   if (vertical_density < horizontal_density) {
    140     *record_rects = horizontal_clustering;
    141     return horizontal_density;
    142   }
    143 
    144   *record_rects = vertical_clustering;
    145   return vertical_density;
    146 }
    147 
    148 }  // namespace
    149 
    150 namespace cc {
    151 
    152 PicturePile::PicturePile()
    153     : is_suitable_for_gpu_rasterization_(true),
    154       pixel_record_distance_(kPixelDistanceToRecord) {
    155 }
    156 
    157 PicturePile::~PicturePile() {
    158 }
    159 
    160 bool PicturePile::UpdateAndExpandInvalidation(
    161     ContentLayerClient* painter,
    162     Region* invalidation,
    163     SkColor background_color,
    164     bool contents_opaque,
    165     bool contents_fill_bounds_completely,
    166     const gfx::Size& layer_size,
    167     const gfx::Rect& visible_layer_rect,
    168     int frame_number,
    169     Picture::RecordingMode recording_mode,
    170     RenderingStatsInstrumentation* stats_instrumentation) {
    171   background_color_ = background_color;
    172   contents_opaque_ = contents_opaque;
    173   contents_fill_bounds_completely_ = contents_fill_bounds_completely;
    174 
    175   bool updated = false;
    176 
    177   Region resize_invalidation;
    178   gfx::Size old_tiling_size = tiling_size();
    179   if (old_tiling_size != layer_size) {
    180     tiling_.SetTilingSize(layer_size);
    181     updated = true;
    182   }
    183 
    184   gfx::Rect interest_rect = visible_layer_rect;
    185   interest_rect.Inset(-pixel_record_distance_, -pixel_record_distance_);
    186   recorded_viewport_ = interest_rect;
    187   recorded_viewport_.Intersect(gfx::Rect(tiling_size()));
    188 
    189   gfx::Rect interest_rect_over_tiles =
    190       tiling_.ExpandRectToTileBounds(interest_rect);
    191 
    192   gfx::Size min_tiling_size(
    193       std::min(tiling_size().width(), old_tiling_size.width()),
    194       std::min(tiling_size().height(), old_tiling_size.height()));
    195   gfx::Size max_tiling_size(
    196       std::max(tiling_size().width(), old_tiling_size.width()),
    197       std::max(tiling_size().height(), old_tiling_size.height()));
    198 
    199   if (old_tiling_size != layer_size) {
    200     has_any_recordings_ = false;
    201 
    202     // Drop recordings that are outside the new or old layer bounds or that
    203     // changed size.  Newly exposed areas are considered invalidated.
    204     // Previously exposed areas that are now outside of bounds also need to
    205     // be invalidated, as they may become part of raster when scale < 1.
    206     std::vector<PictureMapKey> to_erase;
    207     int min_toss_x = tiling_.num_tiles_x();
    208     if (max_tiling_size.width() > min_tiling_size.width()) {
    209       min_toss_x =
    210           tiling_.FirstBorderTileXIndexFromSrcCoord(min_tiling_size.width());
    211     }
    212     int min_toss_y = tiling_.num_tiles_y();
    213     if (max_tiling_size.height() > min_tiling_size.height()) {
    214       min_toss_y =
    215           tiling_.FirstBorderTileYIndexFromSrcCoord(min_tiling_size.height());
    216     }
    217     for (PictureMap::const_iterator it = picture_map_.begin();
    218          it != picture_map_.end();
    219          ++it) {
    220       const PictureMapKey& key = it->first;
    221       if (key.first < min_toss_x && key.second < min_toss_y) {
    222         has_any_recordings_ |= !!it->second.GetPicture();
    223         continue;
    224       }
    225       to_erase.push_back(key);
    226     }
    227 
    228     for (size_t i = 0; i < to_erase.size(); ++i)
    229       picture_map_.erase(to_erase[i]);
    230 
    231     // If a recording is dropped and not re-recorded below, invalidate that
    232     // full recording to cause any raster tiles that would use it to be
    233     // dropped.
    234     // If the recording will be replaced below, invalidate newly exposed
    235     // areas and previously exposed areas to force raster tiles that include the
    236     // old recording to know there is new recording to display.
    237     gfx::Rect min_tiling_rect_over_tiles =
    238         tiling_.ExpandRectToTileBounds(gfx::Rect(min_tiling_size));
    239     if (min_toss_x < tiling_.num_tiles_x()) {
    240       // The bounds which we want to invalidate are the tiles along the old
    241       // edge of the pile when expanding, or the new edge of the pile when
    242       // shrinking. In either case, it's the difference of the two, so we'll
    243       // call this bounding box the DELTA EDGE RECT.
    244       //
    245       // In the picture below, the delta edge rect would be the bounding box of
    246       // tiles {h,i,j}. |min_toss_x| would be equal to the horizontal index of
    247       // the same tiles.
    248       //
    249       //  min pile edge-v  max pile edge-v
    250       // ---------------+ - - - - - - - -+
    251       // mmppssvvyybbeeh|h               .
    252       // mmppssvvyybbeeh|h               .
    253       // nnqqttwwzzccffi|i               .
    254       // nnqqttwwzzccffi|i               .
    255       // oorruuxxaaddggj|j               .
    256       // oorruuxxaaddggj|j               .
    257       // ---------------+ - - - - - - - -+ <- min pile edge
    258       //                                 .
    259       //  - - - - - - - - - - - - - - - -+ <- max pile edge
    260       //
    261       // If you were to slide a vertical beam from the left edge of the
    262       // delta edge rect toward the right, it would either hit the right edge
    263       // of the delta edge rect, or the interest rect (expanded to the bounds
    264       // of the tiles it touches). The same is true for a beam parallel to
    265       // any of the four edges, sliding across the delta edge rect. We use
    266       // the union of these four rectangles generated by these beams to
    267       // determine which part of the delta edge rect is outside of the expanded
    268       // interest rect.
    269       //
    270       // Case 1: Intersect rect is outside the delta edge rect. It can be
    271       // either on the left or the right. The |left_rect| and |right_rect|,
    272       // cover this case, one will be empty and one will cover the full
    273       // delta edge rect. In the picture below, |left_rect| would cover the
    274       // delta edge rect, and |right_rect| would be empty.
    275       // +----------------------+ |^^^^^^^^^^^^^^^|
    276       // |===> DELTA EDGE RECT  | |               |
    277       // |===>                  | | INTEREST RECT |
    278       // |===>                  | |               |
    279       // |===>                  | |               |
    280       // +----------------------+ |vvvvvvvvvvvvvvv|
    281       //
    282       // Case 2: Interest rect is inside the delta edge rect. It will always
    283       // fill the entire delta edge rect horizontally since the old edge rect
    284       // is a single tile wide, and the interest rect has been expanded to the
    285       // bounds of the tiles it touches. In this case the |left_rect| and
    286       // |right_rect| will be empty, but the case is handled by the |top_rect|
    287       // and |bottom_rect|. In the picture below, neither the |top_rect| nor
    288       // |bottom_rect| would empty, they would each cover the area of the old
    289       // edge rect outside the expanded interest rect.
    290       // +-----------------+
    291       // |:::::::::::::::::|
    292       // |:::::::::::::::::|
    293       // |vvvvvvvvvvvvvvvvv|
    294       // |                 |
    295       // +-----------------+
    296       // | INTEREST RECT   |
    297       // |                 |
    298       // +-----------------+
    299       // |                 |
    300       // | DELTA EDGE RECT |
    301       // +-----------------+
    302       //
    303       // Lastly, we need to consider tiles inside the expanded interest rect.
    304       // For those tiles, we want to invalidate exactly the newly exposed
    305       // pixels. In the picture below the tiles in the delta edge rect have
    306       // been resized and the area covered by periods must be invalidated. The
    307       // |exposed_rect| will cover exactly that area.
    308       //           v-min pile edge
    309       // +---------+-------+
    310       // |         ........|
    311       // |         ........|
    312       // | DELTA EDGE.RECT.|
    313       // |         ........|
    314       // |         ........|
    315       // |         ........|
    316       // |         ........|
    317       // |         ........|
    318       // |         ........|
    319       // +---------+-------+
    320 
    321       int left = tiling_.TilePositionX(min_toss_x);
    322       int right = left + tiling_.TileSizeX(min_toss_x);
    323       int top = min_tiling_rect_over_tiles.y();
    324       int bottom = min_tiling_rect_over_tiles.bottom();
    325 
    326       int left_until = std::min(interest_rect_over_tiles.x(), right);
    327       int right_until = std::max(interest_rect_over_tiles.right(), left);
    328       int top_until = std::min(interest_rect_over_tiles.y(), bottom);
    329       int bottom_until = std::max(interest_rect_over_tiles.bottom(), top);
    330 
    331       int exposed_left = min_tiling_size.width();
    332       int exposed_left_until = max_tiling_size.width();
    333       int exposed_top = top;
    334       int exposed_bottom = max_tiling_size.height();
    335       DCHECK_GE(exposed_left, left);
    336 
    337       gfx::Rect left_rect(left, top, left_until - left, bottom - top);
    338       gfx::Rect right_rect(right_until, top, right - right_until, bottom - top);
    339       gfx::Rect top_rect(left, top, right - left, top_until - top);
    340       gfx::Rect bottom_rect(
    341           left, bottom_until, right - left, bottom - bottom_until);
    342       gfx::Rect exposed_rect(exposed_left,
    343                              exposed_top,
    344                              exposed_left_until - exposed_left,
    345                              exposed_bottom - exposed_top);
    346       resize_invalidation.Union(left_rect);
    347       resize_invalidation.Union(right_rect);
    348       resize_invalidation.Union(top_rect);
    349       resize_invalidation.Union(bottom_rect);
    350       resize_invalidation.Union(exposed_rect);
    351     }
    352     if (min_toss_y < tiling_.num_tiles_y()) {
    353       // The same thing occurs here as in the case above, but the invalidation
    354       // rect is the bounding box around the bottom row of tiles in the min
    355       // pile. This would be tiles {o,r,u,x,a,d,g,j} in the above picture.
    356 
    357       int top = tiling_.TilePositionY(min_toss_y);
    358       int bottom = top + tiling_.TileSizeY(min_toss_y);
    359       int left = min_tiling_rect_over_tiles.x();
    360       int right = min_tiling_rect_over_tiles.right();
    361 
    362       int top_until = std::min(interest_rect_over_tiles.y(), bottom);
    363       int bottom_until = std::max(interest_rect_over_tiles.bottom(), top);
    364       int left_until = std::min(interest_rect_over_tiles.x(), right);
    365       int right_until = std::max(interest_rect_over_tiles.right(), left);
    366 
    367       int exposed_top = min_tiling_size.height();
    368       int exposed_top_until = max_tiling_size.height();
    369       int exposed_left = left;
    370       int exposed_right = max_tiling_size.width();
    371       DCHECK_GE(exposed_top, top);
    372 
    373       gfx::Rect left_rect(left, top, left_until - left, bottom - top);
    374       gfx::Rect right_rect(right_until, top, right - right_until, bottom - top);
    375       gfx::Rect top_rect(left, top, right - left, top_until - top);
    376       gfx::Rect bottom_rect(
    377           left, bottom_until, right - left, bottom - bottom_until);
    378       gfx::Rect exposed_rect(exposed_left,
    379                              exposed_top,
    380                              exposed_right - exposed_left,
    381                              exposed_top_until - exposed_top);
    382       resize_invalidation.Union(left_rect);
    383       resize_invalidation.Union(right_rect);
    384       resize_invalidation.Union(top_rect);
    385       resize_invalidation.Union(bottom_rect);
    386       resize_invalidation.Union(exposed_rect);
    387     }
    388   }
    389 
    390   // Detect cases where the full pile is invalidated, in this situation we
    391   // can just drop/invalidate everything.
    392   if (invalidation->Contains(gfx::Rect(old_tiling_size)) ||
    393       invalidation->Contains(gfx::Rect(tiling_size()))) {
    394     for (auto& it : picture_map_)
    395       updated = it.second.Invalidate(frame_number) || updated;
    396   } else {
    397     // Expand invalidation that is on tiles that aren't in the interest rect and
    398     // will not be re-recorded below. These tiles are no longer valid and should
    399     // be considerered fully invalid, so we can know to not keep around raster
    400     // tiles that intersect with these recording tiles.
    401     Region invalidation_expanded_to_full_tiles;
    402 
    403     for (Region::Iterator i(*invalidation); i.has_rect(); i.next()) {
    404       gfx::Rect invalid_rect = i.rect();
    405 
    406       // This rect covers the bounds (excluding borders) of all tiles whose
    407       // bounds (including borders) touch the |interest_rect|. This matches
    408       // the iteration of the |invalid_rect| below which includes borders when
    409       // calling Invalidate() on pictures.
    410       gfx::Rect invalid_rect_outside_interest_rect_tiles =
    411           tiling_.ExpandRectToTileBounds(invalid_rect);
    412       // We subtract the |interest_rect_over_tiles| which represents the bounds
    413       // of tiles that will be re-recorded below. This matches the iteration of
    414       // |interest_rect| below which includes borders.
    415       // TODO(danakj): We should have a Rect-subtract-Rect-to-2-rects operator
    416       // instead of using Rect::Subtract which gives you the bounding box of the
    417       // subtraction.
    418       invalid_rect_outside_interest_rect_tiles.Subtract(
    419           interest_rect_over_tiles);
    420       invalidation_expanded_to_full_tiles.Union(
    421           invalid_rect_outside_interest_rect_tiles);
    422 
    423       // Split this inflated invalidation across tile boundaries and apply it
    424       // to all tiles that it touches.
    425       bool include_borders = true;
    426       for (TilingData::Iterator iter(&tiling_, invalid_rect, include_borders);
    427            iter;
    428            ++iter) {
    429         const PictureMapKey& key = iter.index();
    430 
    431         PictureMap::iterator picture_it = picture_map_.find(key);
    432         if (picture_it == picture_map_.end())
    433           continue;
    434 
    435         // Inform the grid cell that it has been invalidated in this frame.
    436         updated = picture_it->second.Invalidate(frame_number) || updated;
    437         // Invalidate drops the picture so the whole tile better be invalidated
    438         // if it won't be re-recorded below.
    439         DCHECK(tiling_.TileBounds(key.first, key.second)
    440                    .Intersects(interest_rect_over_tiles) ||
    441                invalidation_expanded_to_full_tiles.Contains(
    442                    tiling_.TileBounds(key.first, key.second)));
    443       }
    444     }
    445     invalidation->Union(invalidation_expanded_to_full_tiles);
    446   }
    447 
    448   invalidation->Union(resize_invalidation);
    449 
    450   // Make a list of all invalid tiles; we will attempt to
    451   // cluster these into multiple invalidation regions.
    452   std::vector<gfx::Rect> invalid_tiles;
    453   bool include_borders = true;
    454   for (TilingData::Iterator it(&tiling_, interest_rect, include_borders); it;
    455        ++it) {
    456     const PictureMapKey& key = it.index();
    457     PictureInfo& info = picture_map_[key];
    458 
    459     gfx::Rect rect = PaddedRect(key);
    460     int distance_to_visible =
    461         rect.ManhattanInternalDistance(visible_layer_rect);
    462 
    463     if (info.NeedsRecording(frame_number, distance_to_visible)) {
    464       gfx::Rect tile = tiling_.TileBounds(key.first, key.second);
    465       invalid_tiles.push_back(tile);
    466     } else if (!info.GetPicture()) {
    467       if (recorded_viewport_.Intersects(rect)) {
    468         // Recorded viewport is just an optimization for a fully recorded
    469         // interest rect.  In this case, a tile in that rect has declined
    470         // to be recorded (probably due to frequent invalidations).
    471         // TODO(enne): Shrink the recorded_viewport_ rather than clearing.
    472         recorded_viewport_ = gfx::Rect();
    473       }
    474 
    475       // If a tile in the interest rect is not recorded, the entire tile needs
    476       // to be considered invalid, so that we know not to keep around raster
    477       // tiles that intersect this recording tile.
    478       invalidation->Union(tiling_.TileBounds(it.index_x(), it.index_y()));
    479     }
    480   }
    481 
    482   std::vector<gfx::Rect> record_rects;
    483   ClusterTiles(invalid_tiles, &record_rects);
    484 
    485   if (record_rects.empty())
    486     return updated;
    487 
    488   for (std::vector<gfx::Rect>::iterator it = record_rects.begin();
    489        it != record_rects.end();
    490        it++) {
    491     gfx::Rect record_rect = *it;
    492     record_rect = PadRect(record_rect);
    493 
    494     int repeat_count = std::max(1, slow_down_raster_scale_factor_for_debug_);
    495     scoped_refptr<Picture> picture;
    496 
    497     // Note: Currently, gathering of pixel refs when using a single
    498     // raster thread doesn't provide any benefit. This might change
    499     // in the future but we avoid it for now to reduce the cost of
    500     // Picture::Create.
    501     bool gather_pixel_refs = RasterWorkerPool::GetNumRasterThreads() > 1;
    502 
    503     {
    504       base::TimeDelta best_duration = base::TimeDelta::Max();
    505       for (int i = 0; i < repeat_count; i++) {
    506         base::TimeTicks start_time = stats_instrumentation->StartRecording();
    507         picture = Picture::Create(record_rect,
    508                                   painter,
    509                                   tile_grid_info_,
    510                                   gather_pixel_refs,
    511                                   recording_mode);
    512         // Note the '&&' with previous is-suitable state.
    513         // This means that once a picture-pile becomes unsuitable for gpu
    514         // rasterization due to some content, it will continue to be unsuitable
    515         // even if that content is replaced by gpu-friendly content.
    516         // This is an optimization to avoid iterating though all pictures in
    517         // the pile after each invalidation.
    518         is_suitable_for_gpu_rasterization_ &=
    519             picture->IsSuitableForGpuRasterization();
    520         has_text_ |= picture->HasText();
    521         base::TimeDelta duration =
    522             stats_instrumentation->EndRecording(start_time);
    523         best_duration = std::min(duration, best_duration);
    524       }
    525       int recorded_pixel_count =
    526           picture->LayerRect().width() * picture->LayerRect().height();
    527       stats_instrumentation->AddRecord(best_duration, recorded_pixel_count);
    528     }
    529 
    530     bool found_tile_for_recorded_picture = false;
    531 
    532     bool include_borders = true;
    533     for (TilingData::Iterator it(&tiling_, record_rect, include_borders); it;
    534          ++it) {
    535       const PictureMapKey& key = it.index();
    536       gfx::Rect tile = PaddedRect(key);
    537       if (record_rect.Contains(tile)) {
    538         PictureInfo& info = picture_map_[key];
    539         info.SetPicture(picture);
    540         found_tile_for_recorded_picture = true;
    541       }
    542     }
    543     DetermineIfSolidColor();
    544     DCHECK(found_tile_for_recorded_picture);
    545   }
    546 
    547   has_any_recordings_ = true;
    548   DCHECK(CanRasterSlowTileCheck(recorded_viewport_));
    549   return true;
    550 }
    551 
    552 void PicturePile::SetEmptyBounds() {
    553   tiling_.SetTilingSize(gfx::Size());
    554   picture_map_.clear();
    555   has_any_recordings_ = false;
    556   recorded_viewport_ = gfx::Rect();
    557 }
    558 
    559 void PicturePile::DetermineIfSolidColor() {
    560   is_solid_color_ = false;
    561   solid_color_ = SK_ColorTRANSPARENT;
    562 
    563   if (picture_map_.empty()) {
    564     return;
    565   }
    566 
    567   PictureMap::const_iterator it = picture_map_.begin();
    568   const Picture* picture = it->second.GetPicture();
    569 
    570   // Missing recordings due to frequent invalidations or being too far away
    571   // from the interest rect will cause the a null picture to exist.
    572   if (!picture)
    573     return;
    574 
    575   // Don't bother doing more work if the first image is too complicated.
    576   if (picture->ApproximateOpCount() > kOpCountThatIsOkToAnalyze)
    577     return;
    578 
    579   // Make sure all of the mapped images point to the same picture.
    580   for (++it; it != picture_map_.end(); ++it) {
    581     if (it->second.GetPicture() != picture)
    582       return;
    583   }
    584   skia::AnalysisCanvas canvas(recorded_viewport_.width(),
    585                               recorded_viewport_.height());
    586   canvas.translate(-recorded_viewport_.x(), -recorded_viewport_.y());
    587   picture->Raster(&canvas, nullptr, Region(), 1.0f);
    588   is_solid_color_ = canvas.GetColorIfSolid(&solid_color_);
    589 }
    590 
    591 }  // namespace cc
    592