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