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/benchmark_instrumentation.h"
     11 #include "cc/debug/debug_colors.h"
     12 #include "cc/resources/picture_pile_impl.h"
     13 #include "skia/ext/analysis_canvas.h"
     14 #include "third_party/skia/include/core/SkCanvas.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 PicturePileImpl::ClonesForDrawing::ClonesForDrawing(
     23     const PicturePileImpl* pile, int num_threads) {
     24   for (int i = 0; i < num_threads; i++) {
     25     scoped_refptr<PicturePileImpl> clone =
     26         PicturePileImpl::CreateCloneForDrawing(pile, i);
     27     clones_.push_back(clone);
     28   }
     29 }
     30 
     31 PicturePileImpl::ClonesForDrawing::~ClonesForDrawing() {
     32 }
     33 
     34 scoped_refptr<PicturePileImpl> PicturePileImpl::Create() {
     35   return make_scoped_refptr(new PicturePileImpl);
     36 }
     37 
     38 scoped_refptr<PicturePileImpl> PicturePileImpl::CreateFromOther(
     39     const PicturePileBase* other) {
     40   return make_scoped_refptr(new PicturePileImpl(other));
     41 }
     42 
     43 scoped_refptr<PicturePileImpl> PicturePileImpl::CreateCloneForDrawing(
     44     const PicturePileImpl* other, unsigned thread_index) {
     45   return make_scoped_refptr(new PicturePileImpl(other, thread_index));
     46 }
     47 
     48 PicturePileImpl::PicturePileImpl()
     49     : clones_for_drawing_(ClonesForDrawing(this, 0)) {
     50 }
     51 
     52 PicturePileImpl::PicturePileImpl(const PicturePileBase* other)
     53     : PicturePileBase(other),
     54       clones_for_drawing_(ClonesForDrawing(this, num_raster_threads())) {
     55 }
     56 
     57 PicturePileImpl::PicturePileImpl(
     58     const PicturePileImpl* other, unsigned thread_index)
     59     : PicturePileBase(other, thread_index),
     60       clones_for_drawing_(ClonesForDrawing(this, 0)) {
     61 }
     62 
     63 PicturePileImpl::~PicturePileImpl() {
     64 }
     65 
     66 PicturePileImpl* PicturePileImpl::GetCloneForDrawingOnThread(
     67     unsigned thread_index) const {
     68   CHECK_GT(clones_for_drawing_.clones_.size(), thread_index);
     69   return clones_for_drawing_.clones_[thread_index].get();
     70 }
     71 
     72 void PicturePileImpl::RasterDirect(
     73     SkCanvas* canvas,
     74     gfx::Rect canvas_rect,
     75     float contents_scale,
     76     RasterStats* raster_stats) {
     77   RasterCommon(canvas, NULL, canvas_rect, contents_scale, raster_stats);
     78 }
     79 
     80 void PicturePileImpl::RasterForAnalysis(
     81     skia::AnalysisCanvas* canvas,
     82     gfx::Rect canvas_rect,
     83     float contents_scale) {
     84   RasterCommon(canvas, canvas, canvas_rect, contents_scale, NULL);
     85 }
     86 
     87 void PicturePileImpl::RasterToBitmap(
     88     SkCanvas* canvas,
     89     gfx::Rect canvas_rect,
     90     float contents_scale,
     91     RasterStats* raster_stats) {
     92 #ifndef NDEBUG
     93   // Any non-painted areas will be left in this color.
     94   canvas->clear(DebugColors::NonPaintedFillColor());
     95 #endif  // NDEBUG
     96 
     97   // If this picture has opaque contents, it is guaranteeing that it will
     98   // draw an opaque rect the size of the layer.  If it is not, then we must
     99   // clear this canvas ourselves.
    100   if (!contents_opaque_) {
    101     // Clearing is about ~4x faster than drawing a rect even if the content
    102     // isn't covering a majority of the canvas.
    103     canvas->clear(SK_ColorTRANSPARENT);
    104   } else {
    105     // Even if it is opaque, on any rasterizations that touch the edge of the
    106     // layer, we also need to raster the background color underneath the last
    107     // texel (since the recording won't cover it) and outside the last texel
    108     // (due to linear filtering when using this texture).
    109     gfx::SizeF total_content_size = gfx::ScaleSize(tiling_.total_size(),
    110                                                    contents_scale);
    111     gfx::Rect content_rect(gfx::ToCeiledSize(total_content_size));
    112     gfx::Rect deflated_content_rect = content_rect;
    113     content_rect.Intersect(canvas_rect);
    114 
    115     // The final texel of content may only be partially covered by a
    116     // rasterization; this rect represents the content rect that is fully
    117     // covered by content.
    118     deflated_content_rect.Inset(0, 0, 1, 1);
    119     deflated_content_rect.Intersect(canvas_rect);
    120     if (!deflated_content_rect.Contains(canvas_rect)) {
    121       // Drawing at most 2 x 2 x (canvas width + canvas height) texels is 2-3X
    122       // faster than clearing, so special case this.
    123       canvas->save();
    124       gfx::Rect inflated_content_rect = content_rect;
    125       inflated_content_rect.Inset(0, 0, -1, -1);
    126       canvas->clipRect(gfx::RectToSkRect(inflated_content_rect),
    127                        SkRegion::kReplace_Op);
    128       canvas->clipRect(gfx::RectToSkRect(deflated_content_rect),
    129                        SkRegion::kDifference_Op);
    130       canvas->drawColor(background_color_, SkXfermode::kSrc_Mode);
    131       canvas->restore();
    132     }
    133   }
    134 
    135   RasterCommon(canvas, NULL, canvas_rect, contents_scale, raster_stats);
    136 }
    137 
    138 void PicturePileImpl::RasterCommon(
    139     SkCanvas* canvas,
    140     SkDrawPictureCallback* callback,
    141     gfx::Rect canvas_rect,
    142     float contents_scale,
    143     RasterStats* raster_stats) {
    144   DCHECK(contents_scale >= min_contents_scale_);
    145 
    146   canvas->translate(-canvas_rect.x(), -canvas_rect.y());
    147 
    148   gfx::SizeF total_content_size = gfx::ScaleSize(tiling_.total_size(),
    149                                                  contents_scale);
    150   gfx::Rect total_content_rect(gfx::ToCeiledSize(total_content_size));
    151   gfx::Rect content_rect = total_content_rect;
    152   content_rect.Intersect(canvas_rect);
    153 
    154   // Rasterize the collection of relevant picture piles.
    155   gfx::Rect layer_rect = gfx::ScaleToEnclosingRect(
    156       content_rect, 1.f / contents_scale);
    157 
    158   canvas->clipRect(gfx::RectToSkRect(content_rect),
    159                    SkRegion::kIntersect_Op);
    160   Region unclipped(content_rect);
    161 
    162   if (raster_stats) {
    163     raster_stats->total_pixels_rasterized = 0;
    164     raster_stats->total_rasterize_time = base::TimeDelta::FromSeconds(0);
    165     raster_stats->best_rasterize_time = base::TimeDelta::FromSeconds(0);
    166   }
    167 
    168   for (TilingData::Iterator tile_iter(&tiling_, layer_rect);
    169        tile_iter; ++tile_iter) {
    170     PictureListMap::iterator map_iter =
    171         picture_list_map_.find(tile_iter.index());
    172     if (map_iter == picture_list_map_.end())
    173       continue;
    174     PictureList& pic_list= map_iter->second;
    175     if (pic_list.empty())
    176       continue;
    177 
    178     // Raster through the picture list top down, using clips to make sure that
    179     // pictures on top are not overdrawn by pictures on the bottom.
    180     for (PictureList::reverse_iterator i = pic_list.rbegin();
    181          i != pic_list.rend(); ++i) {
    182       // This is intentionally *enclosed* rect, so that the clip is aligned on
    183       // integral post-scale content pixels and does not extend past the edges
    184       // of the picture's layer rect.  The min_contents_scale enforces that
    185       // enough buffer pixels have been added such that the enclosed rect
    186       // encompasses all invalidated pixels at any larger scale level.
    187       gfx::Rect content_clip = gfx::ScaleToEnclosedRect(
    188           (*i)->LayerRect(), contents_scale);
    189       DCHECK(!content_clip.IsEmpty()) <<
    190           "Layer rect: " << (*i)->LayerRect().ToString() <<
    191           "Contents scale: " << contents_scale;
    192       if (!unclipped.Intersects(content_clip))
    193         continue;
    194 
    195       base::TimeDelta total_duration =
    196           base::TimeDelta::FromInternalValue(0);
    197       base::TimeDelta best_duration =
    198           base::TimeDelta::FromInternalValue(std::numeric_limits<int64>::max());
    199       int repeat_count = std::max(1, slow_down_raster_scale_factor_for_debug_);
    200 
    201       TRACE_EVENT0(benchmark_instrumentation::kCategory,
    202                    benchmark_instrumentation::kRasterLoop);
    203       for (int j = 0; j < repeat_count; ++j) {
    204         base::TimeTicks start_time;
    205         if (raster_stats)
    206           start_time = base::TimeTicks::HighResNow();
    207 
    208         (*i)->Raster(canvas, callback, content_clip, contents_scale);
    209 
    210         if (raster_stats) {
    211           base::TimeDelta duration = base::TimeTicks::HighResNow() - start_time;
    212           total_duration += duration;
    213           best_duration = std::min(best_duration, duration);
    214         }
    215       }
    216 
    217       if (raster_stats) {
    218         gfx::Rect raster_rect = canvas_rect;
    219         raster_rect.Intersect(content_clip);
    220         raster_stats->total_pixels_rasterized +=
    221             repeat_count * raster_rect.width() * raster_rect.height();
    222         raster_stats->total_rasterize_time += total_duration;
    223         raster_stats->best_rasterize_time += best_duration;
    224       }
    225 
    226       if (show_debug_picture_borders_) {
    227         gfx::Rect border = content_clip;
    228         border.Inset(0, 0, 1, 1);
    229 
    230         SkPaint picture_border_paint;
    231         picture_border_paint.setColor(DebugColors::PictureBorderColor());
    232         canvas->drawLine(border.x(), border.y(), border.right(), border.y(),
    233                          picture_border_paint);
    234         canvas->drawLine(border.right(), border.y(), border.right(),
    235                          border.bottom(), picture_border_paint);
    236         canvas->drawLine(border.right(), border.bottom(), border.x(),
    237                          border.bottom(), picture_border_paint);
    238         canvas->drawLine(border.x(), border.bottom(), border.x(), border.y(),
    239                          picture_border_paint);
    240       }
    241 
    242       // Don't allow pictures underneath to draw where this picture did.
    243       canvas->clipRect(
    244           gfx::RectToSkRect(content_clip),
    245           SkRegion::kDifference_Op);
    246       unclipped.Subtract(content_clip);
    247     }
    248   }
    249 
    250 #ifndef NDEBUG
    251   // Fill the remaining clip with debug color. This allows us to
    252   // distinguish between non painted areas and problems with missing
    253   // pictures.
    254   SkPaint paint;
    255   paint.setColor(DebugColors::MissingPictureFillColor());
    256   paint.setXfermodeMode(SkXfermode::kSrc_Mode);
    257   canvas->drawPaint(paint);
    258 #endif  // NDEBUG
    259 
    260   // We should always paint some part of |content_rect|.
    261   DCHECK(!unclipped.Contains(content_rect));
    262 }
    263 
    264 skia::RefPtr<SkPicture> PicturePileImpl::GetFlattenedPicture() {
    265   TRACE_EVENT0("cc", "PicturePileImpl::GetFlattenedPicture");
    266 
    267   gfx::Rect layer_rect(tiling_.total_size());
    268   skia::RefPtr<SkPicture> picture = skia::AdoptRef(new SkPicture);
    269   if (layer_rect.IsEmpty())
    270     return picture;
    271 
    272   SkCanvas* canvas = picture->beginRecording(
    273       layer_rect.width(),
    274       layer_rect.height(),
    275       SkPicture::kUsePathBoundsForClip_RecordingFlag);
    276 
    277   RasterToBitmap(canvas, layer_rect, 1.0, NULL);
    278   picture->endRecording();
    279 
    280   return picture;
    281 }
    282 
    283 void PicturePileImpl::AnalyzeInRect(gfx::Rect content_rect,
    284                                     float contents_scale,
    285                                     PicturePileImpl::Analysis* analysis) {
    286   DCHECK(analysis);
    287   TRACE_EVENT0("cc", "PicturePileImpl::AnalyzeInRect");
    288 
    289   gfx::Rect layer_rect = gfx::ScaleToEnclosingRect(
    290       content_rect, 1.0f / contents_scale);
    291 
    292   layer_rect.Intersect(gfx::Rect(tiling_.total_size()));
    293 
    294   SkBitmap empty_bitmap;
    295   empty_bitmap.setConfig(SkBitmap::kNo_Config,
    296                          layer_rect.width(),
    297                          layer_rect.height());
    298   skia::AnalysisDevice device(empty_bitmap);
    299   skia::AnalysisCanvas canvas(&device);
    300 
    301   RasterForAnalysis(&canvas, layer_rect, 1.0f);
    302 
    303   analysis->is_solid_color = canvas.GetColorIfSolid(&analysis->solid_color);
    304   analysis->has_text = canvas.HasText();
    305 }
    306 
    307 PicturePileImpl::Analysis::Analysis()
    308     : is_solid_color(false),
    309       has_text(false) {
    310 }
    311 
    312 PicturePileImpl::Analysis::~Analysis() {
    313 }
    314 
    315 PicturePileImpl::PixelRefIterator::PixelRefIterator(
    316     gfx::Rect content_rect,
    317     float contents_scale,
    318     const PicturePileImpl* picture_pile)
    319     : picture_pile_(picture_pile),
    320       layer_rect_(gfx::ScaleToEnclosingRect(
    321           content_rect, 1.f / contents_scale)),
    322       tile_iterator_(&picture_pile_->tiling_, layer_rect_),
    323       picture_list_(NULL) {
    324   // Early out if there isn't a single tile.
    325   if (!tile_iterator_)
    326     return;
    327 
    328   if (AdvanceToTileWithPictures())
    329     AdvanceToPictureWithPixelRefs();
    330 }
    331 
    332 PicturePileImpl::PixelRefIterator::~PixelRefIterator() {
    333 }
    334 
    335 PicturePileImpl::PixelRefIterator&
    336     PicturePileImpl::PixelRefIterator::operator++() {
    337   ++pixel_ref_iterator_;
    338   if (pixel_ref_iterator_)
    339     return *this;
    340 
    341   ++picture_list_iterator_;
    342   AdvanceToPictureWithPixelRefs();
    343   return *this;
    344 }
    345 
    346 bool PicturePileImpl::PixelRefIterator::AdvanceToTileWithPictures() {
    347   for (; tile_iterator_; ++tile_iterator_) {
    348     PictureListMap::const_iterator map_iterator =
    349         picture_pile_->picture_list_map_.find(tile_iterator_.index());
    350     if (map_iterator != picture_pile_->picture_list_map_.end()) {
    351       picture_list_ = &map_iterator->second;
    352       picture_list_iterator_ = picture_list_->begin();
    353       return true;
    354     }
    355   }
    356 
    357   return false;
    358 }
    359 
    360 void PicturePileImpl::PixelRefIterator::AdvanceToPictureWithPixelRefs() {
    361   DCHECK(tile_iterator_);
    362   do {
    363     for (;
    364          picture_list_iterator_ != picture_list_->end();
    365          ++picture_list_iterator_) {
    366       pixel_ref_iterator_ =
    367           Picture::PixelRefIterator(layer_rect_, picture_list_iterator_->get());
    368       if (pixel_ref_iterator_)
    369         return;
    370     }
    371     ++tile_iterator_;
    372   } while (AdvanceToTileWithPictures());
    373 }
    374 
    375 void PicturePileImpl::DidBeginTracing() {
    376   gfx::Rect layer_rect(tiling_.total_size());
    377   for (PictureListMap::iterator pli = picture_list_map_.begin();
    378        pli != picture_list_map_.end();
    379        pli++) {
    380     PictureList& picture_list = (*pli).second;
    381     for (PictureList::iterator picture = picture_list.begin();
    382          picture != picture_list.end();
    383          picture++) {
    384       (*picture)->EmitTraceSnapshot();
    385     }
    386   }
    387 }
    388 
    389 }  // namespace cc
    390