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