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