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 <algorithm>
      6 #include <limits>
      7 
      8 #include "base/debug/trace_event.h"
      9 #include "cc/base/region.h"
     10 #include "cc/debug/debug_colors.h"
     11 #include "cc/resources/picture_pile_impl.h"
     12 #include "skia/ext/analysis_canvas.h"
     13 #include "third_party/skia/include/core/SkCanvas.h"
     14 #include "third_party/skia/include/core/SkPictureRecorder.h"
     15 #include "third_party/skia/include/core/SkSize.h"
     16 #include "ui/gfx/rect_conversions.h"
     17 #include "ui/gfx/size_conversions.h"
     18 #include "ui/gfx/skia_util.h"
     19 
     20 namespace cc {
     21 
     22 scoped_refptr<PicturePileImpl> PicturePileImpl::Create() {
     23   return make_scoped_refptr(new PicturePileImpl);
     24 }
     25 
     26 scoped_refptr<PicturePileImpl> PicturePileImpl::CreateFromOther(
     27     const PicturePileBase* other) {
     28   return make_scoped_refptr(new PicturePileImpl(other));
     29 }
     30 
     31 PicturePileImpl::PicturePileImpl() {
     32 }
     33 
     34 PicturePileImpl::PicturePileImpl(const PicturePileBase* other)
     35     : PicturePileBase(other) {
     36 }
     37 
     38 PicturePileImpl::~PicturePileImpl() {
     39 }
     40 
     41 void PicturePileImpl::RasterDirect(
     42     SkCanvas* canvas,
     43     const gfx::Rect& canvas_rect,
     44     float contents_scale,
     45     RenderingStatsInstrumentation* rendering_stats_instrumentation) {
     46   RasterCommon(canvas,
     47                NULL,
     48                canvas_rect,
     49                contents_scale,
     50                rendering_stats_instrumentation,
     51                false);
     52 }
     53 
     54 void PicturePileImpl::RasterForAnalysis(
     55     skia::AnalysisCanvas* canvas,
     56     const gfx::Rect& canvas_rect,
     57     float contents_scale,
     58     RenderingStatsInstrumentation* stats_instrumentation) const {
     59   RasterCommon(
     60       canvas, canvas, canvas_rect, contents_scale, stats_instrumentation, true);
     61 }
     62 
     63 void PicturePileImpl::RasterToBitmap(
     64     SkCanvas* canvas,
     65     const gfx::Rect& canvas_rect,
     66     float contents_scale,
     67     RenderingStatsInstrumentation* rendering_stats_instrumentation) const {
     68   canvas->discard();
     69   if (clear_canvas_with_debug_color_) {
     70     // Any non-painted areas in the content bounds will be left in this color.
     71     canvas->clear(DebugColors::NonPaintedFillColor());
     72   }
     73 
     74   // If this picture has opaque contents, it is guaranteeing that it will
     75   // draw an opaque rect the size of the layer.  If it is not, then we must
     76   // clear this canvas ourselves.
     77   if (contents_opaque_ || contents_fill_bounds_completely_) {
     78     // Even if completely covered, for rasterizations that touch the edge of the
     79     // layer, we also need to raster the background color underneath the last
     80     // texel (since the recording won't cover it) and outside the last texel
     81     // (due to linear filtering when using this texture).
     82     gfx::Rect content_tiling_rect = gfx::ToEnclosingRect(
     83         gfx::ScaleRect(gfx::Rect(tiling_.tiling_size()), contents_scale));
     84 
     85     // The final texel of content may only be partially covered by a
     86     // rasterization; this rect represents the content rect that is fully
     87     // covered by content.
     88     gfx::Rect deflated_content_tiling_rect = content_tiling_rect;
     89     deflated_content_tiling_rect.Inset(0, 0, 1, 1);
     90     if (!deflated_content_tiling_rect.Contains(canvas_rect)) {
     91       if (clear_canvas_with_debug_color_) {
     92         // Any non-painted areas outside of the content bounds are left in
     93         // this color.  If this is seen then it means that cc neglected to
     94         // rerasterize a tile that used to intersect with the content rect
     95         // after the content bounds grew.
     96         canvas->save();
     97         canvas->translate(-canvas_rect.x(), -canvas_rect.y());
     98         canvas->clipRect(gfx::RectToSkRect(content_tiling_rect),
     99                          SkRegion::kDifference_Op);
    100         canvas->drawColor(DebugColors::MissingResizeInvalidations(),
    101                           SkXfermode::kSrc_Mode);
    102         canvas->restore();
    103       }
    104 
    105       // Drawing at most 2 x 2 x (canvas width + canvas height) texels is 2-3X
    106       // faster than clearing, so special case this.
    107       canvas->save();
    108       canvas->translate(-canvas_rect.x(), -canvas_rect.y());
    109       gfx::Rect inflated_content_tiling_rect = content_tiling_rect;
    110       inflated_content_tiling_rect.Inset(0, 0, -1, -1);
    111       canvas->clipRect(gfx::RectToSkRect(inflated_content_tiling_rect),
    112                        SkRegion::kReplace_Op);
    113       canvas->clipRect(gfx::RectToSkRect(deflated_content_tiling_rect),
    114                        SkRegion::kDifference_Op);
    115       canvas->drawColor(background_color_, SkXfermode::kSrc_Mode);
    116       canvas->restore();
    117     }
    118   } else {
    119     TRACE_EVENT_INSTANT0("cc", "SkCanvas::clear", TRACE_EVENT_SCOPE_THREAD);
    120     // Clearing is about ~4x faster than drawing a rect even if the content
    121     // isn't covering a majority of the canvas.
    122     canvas->clear(SK_ColorTRANSPARENT);
    123   }
    124 
    125   RasterCommon(canvas,
    126                NULL,
    127                canvas_rect,
    128                contents_scale,
    129                rendering_stats_instrumentation,
    130                false);
    131 }
    132 
    133 void PicturePileImpl::CoalesceRasters(const gfx::Rect& canvas_rect,
    134                                       const gfx::Rect& content_rect,
    135                                       float contents_scale,
    136                                       PictureRegionMap* results) const {
    137   DCHECK(results);
    138   // Rasterize the collection of relevant picture piles.
    139   gfx::Rect layer_rect = gfx::ScaleToEnclosingRect(
    140       content_rect, 1.f / contents_scale);
    141 
    142   // Make sure pictures don't overlap by keeping track of previous right/bottom.
    143   int min_content_left = -1;
    144   int min_content_top = -1;
    145   int last_row_index = -1;
    146   int last_col_index = -1;
    147   gfx::Rect last_content_rect;
    148 
    149   // Coalesce rasters of the same picture into different rects:
    150   //  - Compute the clip of each of the pile chunks,
    151   //  - Subtract it from the canvas rect to get difference region
    152   //  - Later, use the difference region to subtract each of the comprising
    153   //    rects from the canvas.
    154   // Note that in essence, we're trying to mimic clipRegion with intersect op
    155   // that also respects the current canvas transform and clip. In order to use
    156   // the canvas transform, we must stick to clipRect operations (clipRegion
    157   // ignores the transform). Intersect then can be written as subtracting the
    158   // negation of the region we're trying to intersect. Luckily, we know that all
    159   // of the rects will have to fit into |content_rect|, so we can start with
    160   // that and subtract chunk rects to get the region that we need to subtract
    161   // from the canvas. Then, we can use clipRect with difference op to subtract
    162   // each rect in the region.
    163   bool include_borders = true;
    164   for (TilingData::Iterator tile_iter(&tiling_, layer_rect, include_borders);
    165        tile_iter;
    166        ++tile_iter) {
    167     PictureMap::const_iterator map_iter = picture_map_.find(tile_iter.index());
    168     if (map_iter == picture_map_.end())
    169       continue;
    170     const PictureInfo& info = map_iter->second;
    171     const Picture* picture = info.GetPicture();
    172     if (!picture)
    173       continue;
    174 
    175     // This is intentionally *enclosed* rect, so that the clip is aligned on
    176     // integral post-scale content pixels and does not extend past the edges
    177     // of the picture chunk's layer rect.  The min_contents_scale enforces that
    178     // enough buffer pixels have been added such that the enclosed rect
    179     // encompasses all invalidated pixels at any larger scale level.
    180     gfx::Rect chunk_rect = PaddedRect(tile_iter.index());
    181     gfx::Rect content_clip =
    182         gfx::ScaleToEnclosedRect(chunk_rect, contents_scale);
    183     DCHECK(!content_clip.IsEmpty()) << "Layer rect: "
    184                                     << picture->LayerRect().ToString()
    185                                     << "Contents scale: " << contents_scale;
    186     content_clip.Intersect(canvas_rect);
    187 
    188     // Make sure iterator goes top->bottom.
    189     DCHECK_GE(tile_iter.index_y(), last_row_index);
    190     if (tile_iter.index_y() > last_row_index) {
    191       // First tile in a new row.
    192       min_content_left = content_clip.x();
    193       min_content_top = last_content_rect.bottom();
    194     } else {
    195       // Make sure iterator goes left->right.
    196       DCHECK_GT(tile_iter.index_x(), last_col_index);
    197       min_content_left = last_content_rect.right();
    198       min_content_top = last_content_rect.y();
    199     }
    200 
    201     last_col_index = tile_iter.index_x();
    202     last_row_index = tile_iter.index_y();
    203 
    204     // Only inset if the content_clip is less than then previous min.
    205     int inset_left = std::max(0, min_content_left - content_clip.x());
    206     int inset_top = std::max(0, min_content_top - content_clip.y());
    207     content_clip.Inset(inset_left, inset_top, 0, 0);
    208 
    209     PictureRegionMap::iterator it = results->find(picture);
    210     Region* clip_region;
    211     if (it == results->end()) {
    212       // The clip for a set of coalesced pictures starts out clipping the entire
    213       // canvas.  Each picture added to the set must subtract its own bounds
    214       // from the clip region, poking a hole so that the picture is unclipped.
    215       clip_region = &(*results)[picture];
    216       *clip_region = canvas_rect;
    217     } else {
    218       clip_region = &it->second;
    219     }
    220 
    221     DCHECK(clip_region->Contains(content_clip))
    222         << "Content clips should not overlap.";
    223     clip_region->Subtract(content_clip);
    224     last_content_rect = content_clip;
    225   }
    226 }
    227 
    228 void PicturePileImpl::RasterCommon(
    229     SkCanvas* canvas,
    230     SkDrawPictureCallback* callback,
    231     const gfx::Rect& canvas_rect,
    232     float contents_scale,
    233     RenderingStatsInstrumentation* rendering_stats_instrumentation,
    234     bool is_analysis) const {
    235   DCHECK(contents_scale >= min_contents_scale_);
    236 
    237   canvas->translate(-canvas_rect.x(), -canvas_rect.y());
    238   gfx::Rect content_tiling_rect = gfx::ToEnclosingRect(
    239       gfx::ScaleRect(gfx::Rect(tiling_.tiling_size()), contents_scale));
    240   content_tiling_rect.Intersect(canvas_rect);
    241 
    242   canvas->clipRect(gfx::RectToSkRect(content_tiling_rect),
    243                    SkRegion::kIntersect_Op);
    244 
    245   PictureRegionMap picture_region_map;
    246   CoalesceRasters(
    247       canvas_rect, content_tiling_rect, contents_scale, &picture_region_map);
    248 
    249 #ifndef NDEBUG
    250   Region total_clip;
    251 #endif  // NDEBUG
    252 
    253   // Iterate the coalesced map and use each picture's region
    254   // to clip the canvas.
    255   for (PictureRegionMap::iterator it = picture_region_map.begin();
    256        it != picture_region_map.end();
    257        ++it) {
    258     const Picture* picture = it->first;
    259     Region negated_clip_region = it->second;
    260 
    261 #ifndef NDEBUG
    262     Region positive_clip = content_tiling_rect;
    263     positive_clip.Subtract(negated_clip_region);
    264     // Make sure we never rasterize the same region twice.
    265     DCHECK(!total_clip.Intersects(positive_clip));
    266     total_clip.Union(positive_clip);
    267 #endif  // NDEBUG
    268 
    269     base::TimeDelta best_duration = base::TimeDelta::Max();
    270     int repeat_count = std::max(1, slow_down_raster_scale_factor_for_debug_);
    271     int rasterized_pixel_count = 0;
    272 
    273     for (int j = 0; j < repeat_count; ++j) {
    274       base::TimeTicks start_time;
    275       if (rendering_stats_instrumentation)
    276         start_time = rendering_stats_instrumentation->StartRecording();
    277 
    278       rasterized_pixel_count = picture->Raster(
    279           canvas, callback, negated_clip_region, contents_scale);
    280 
    281       if (rendering_stats_instrumentation) {
    282         base::TimeDelta duration =
    283             rendering_stats_instrumentation->EndRecording(start_time);
    284         best_duration = std::min(best_duration, duration);
    285       }
    286     }
    287 
    288     if (rendering_stats_instrumentation) {
    289       if (is_analysis) {
    290         rendering_stats_instrumentation->AddAnalysis(best_duration,
    291                                                      rasterized_pixel_count);
    292       } else {
    293         rendering_stats_instrumentation->AddRaster(best_duration,
    294                                                    rasterized_pixel_count);
    295       }
    296     }
    297   }
    298 
    299 #ifndef NDEBUG
    300   // Fill the clip with debug color. This allows us to
    301   // distinguish between non painted areas and problems with missing
    302   // pictures.
    303   SkPaint paint;
    304   for (Region::Iterator it(total_clip); it.has_rect(); it.next())
    305     canvas->clipRect(gfx::RectToSkRect(it.rect()), SkRegion::kDifference_Op);
    306   paint.setColor(DebugColors::MissingPictureFillColor());
    307   paint.setXfermodeMode(SkXfermode::kSrc_Mode);
    308   canvas->drawPaint(paint);
    309 #endif  // NDEBUG
    310 }
    311 
    312 skia::RefPtr<SkPicture> PicturePileImpl::GetFlattenedPicture() {
    313   TRACE_EVENT0("cc", "PicturePileImpl::GetFlattenedPicture");
    314 
    315   gfx::Rect tiling_rect(tiling_.tiling_size());
    316   SkPictureRecorder recorder;
    317   SkCanvas* canvas =
    318       recorder.beginRecording(tiling_rect.width(), tiling_rect.height());
    319   if (!tiling_rect.IsEmpty())
    320     RasterToBitmap(canvas, tiling_rect, 1.0, NULL);
    321   skia::RefPtr<SkPicture> picture = skia::AdoptRef(recorder.endRecording());
    322 
    323   return picture;
    324 }
    325 
    326 void PicturePileImpl::AnalyzeInRect(const gfx::Rect& content_rect,
    327                                     float contents_scale,
    328                                     PicturePileImpl::Analysis* analysis) const {
    329   AnalyzeInRect(content_rect, contents_scale, analysis, NULL);
    330 }
    331 
    332 void PicturePileImpl::AnalyzeInRect(
    333     const gfx::Rect& content_rect,
    334     float contents_scale,
    335     PicturePileImpl::Analysis* analysis,
    336     RenderingStatsInstrumentation* stats_instrumentation) const {
    337   DCHECK(analysis);
    338   TRACE_EVENT0("cc", "PicturePileImpl::AnalyzeInRect");
    339 
    340   gfx::Rect layer_rect = gfx::ScaleToEnclosingRect(
    341       content_rect, 1.0f / contents_scale);
    342 
    343   layer_rect.Intersect(gfx::Rect(tiling_.tiling_size()));
    344 
    345   skia::AnalysisCanvas canvas(layer_rect.width(), layer_rect.height());
    346 
    347   RasterForAnalysis(&canvas, layer_rect, 1.0f, stats_instrumentation);
    348 
    349   analysis->is_solid_color = canvas.GetColorIfSolid(&analysis->solid_color);
    350 }
    351 
    352 // Since there are situations when we can skip analysis, the variables have to
    353 // be set to their safest values. That is, we have to assume that the tile is
    354 // not solid color. As well, we have to assume that the tile has text so we
    355 // don't early out incorrectly.
    356 PicturePileImpl::Analysis::Analysis() : is_solid_color(false) {
    357 }
    358 
    359 PicturePileImpl::Analysis::~Analysis() {
    360 }
    361 
    362 PicturePileImpl::PixelRefIterator::PixelRefIterator(
    363     const gfx::Rect& content_rect,
    364     float contents_scale,
    365     const PicturePileImpl* picture_pile)
    366     : picture_pile_(picture_pile),
    367       layer_rect_(
    368           gfx::ScaleToEnclosingRect(content_rect, 1.f / contents_scale)),
    369       tile_iterator_(&picture_pile_->tiling_,
    370                      layer_rect_,
    371                      false /* include_borders */) {
    372   // Early out if there isn't a single tile.
    373   if (!tile_iterator_)
    374     return;
    375 
    376   AdvanceToTilePictureWithPixelRefs();
    377 }
    378 
    379 PicturePileImpl::PixelRefIterator::~PixelRefIterator() {
    380 }
    381 
    382 PicturePileImpl::PixelRefIterator&
    383     PicturePileImpl::PixelRefIterator::operator++() {
    384   ++pixel_ref_iterator_;
    385   if (pixel_ref_iterator_)
    386     return *this;
    387 
    388   ++tile_iterator_;
    389   AdvanceToTilePictureWithPixelRefs();
    390   return *this;
    391 }
    392 
    393 void PicturePileImpl::PixelRefIterator::AdvanceToTilePictureWithPixelRefs() {
    394   for (; tile_iterator_; ++tile_iterator_) {
    395     PictureMap::const_iterator it =
    396         picture_pile_->picture_map_.find(tile_iterator_.index());
    397     if (it == picture_pile_->picture_map_.end())
    398       continue;
    399 
    400     const Picture* picture = it->second.GetPicture();
    401     if (!picture || (processed_pictures_.count(picture) != 0) ||
    402         !picture->WillPlayBackBitmaps())
    403       continue;
    404 
    405     processed_pictures_.insert(picture);
    406     pixel_ref_iterator_ = Picture::PixelRefIterator(layer_rect_, picture);
    407     if (pixel_ref_iterator_)
    408       break;
    409   }
    410 }
    411 
    412 void PicturePileImpl::DidBeginTracing() {
    413   std::set<const void*> processed_pictures;
    414   for (PictureMap::iterator it = picture_map_.begin();
    415        it != picture_map_.end();
    416        ++it) {
    417     const Picture* picture = it->second.GetPicture();
    418     if (picture && (processed_pictures.count(picture) == 0)) {
    419       picture->EmitTraceSnapshot();
    420       processed_pictures.insert(picture);
    421     }
    422   }
    423 }
    424 
    425 }  // namespace cc
    426