• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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 
ClonesForDrawing(const PicturePileImpl * pile,int num_threads)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 
~ClonesForDrawing()32 PicturePileImpl::ClonesForDrawing::~ClonesForDrawing() {
33 }
34 
Create()35 scoped_refptr<PicturePileImpl> PicturePileImpl::Create() {
36   return make_scoped_refptr(new PicturePileImpl);
37 }
38 
CreateFromOther(const PicturePileBase * other)39 scoped_refptr<PicturePileImpl> PicturePileImpl::CreateFromOther(
40     const PicturePileBase* other) {
41   return make_scoped_refptr(new PicturePileImpl(other));
42 }
43 
CreateCloneForDrawing(const PicturePileImpl * other,unsigned thread_index)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 
PicturePileImpl()49 PicturePileImpl::PicturePileImpl()
50     : clones_for_drawing_(ClonesForDrawing(this, 0)) {
51 }
52 
PicturePileImpl(const PicturePileBase * other)53 PicturePileImpl::PicturePileImpl(const PicturePileBase* other)
54     : PicturePileBase(other),
55       clones_for_drawing_(ClonesForDrawing(
56                               this, RasterWorkerPool::GetNumRasterThreads())) {
57 }
58 
PicturePileImpl(const PicturePileImpl * other,unsigned thread_index)59 PicturePileImpl::PicturePileImpl(
60     const PicturePileImpl* other, unsigned thread_index)
61     : PicturePileBase(other, thread_index),
62       clones_for_drawing_(ClonesForDrawing(this, 0)) {
63 }
64 
~PicturePileImpl()65 PicturePileImpl::~PicturePileImpl() {
66 }
67 
GetCloneForDrawingOnThread(unsigned thread_index) const68 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 
RasterDirect(SkCanvas * canvas,const gfx::Rect & canvas_rect,float contents_scale,RenderingStatsInstrumentation * rendering_stats_instrumentation)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 
RasterForAnalysis(skia::AnalysisCanvas * canvas,const gfx::Rect & canvas_rect,float contents_scale,RenderingStatsInstrumentation * stats_instrumentation)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 
RasterToBitmap(SkCanvas * canvas,const gfx::Rect & canvas_rect,float contents_scale,RenderingStatsInstrumentation * rendering_stats_instrumentation)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 
CoalesceRasters(const gfx::Rect & canvas_rect,const gfx::Rect & content_rect,float contents_scale,PictureRegionMap * results)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 
RasterCommon(SkCanvas * canvas,SkDrawPictureCallback * callback,const gfx::Rect & canvas_rect,float contents_scale,RenderingStatsInstrumentation * rendering_stats_instrumentation,bool is_analysis)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 
GetFlattenedPicture()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 
AnalyzeInRect(const gfx::Rect & content_rect,float contents_scale,PicturePileImpl::Analysis * analysis)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 
AnalyzeInRect(const gfx::Rect & content_rect,float contents_scale,PicturePileImpl::Analysis * analysis,RenderingStatsInstrumentation * stats_instrumentation)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.
Analysis()390 PicturePileImpl::Analysis::Analysis() : is_solid_color(false) {
391 }
392 
~Analysis()393 PicturePileImpl::Analysis::~Analysis() {
394 }
395 
PixelRefIterator(const gfx::Rect & content_rect,float contents_scale,const PicturePileImpl * picture_pile)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 
~PixelRefIterator()413 PicturePileImpl::PixelRefIterator::~PixelRefIterator() {
414 }
415 
416 PicturePileImpl::PixelRefIterator&
operator ++()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 
AdvanceToTilePictureWithPixelRefs()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 
DidBeginTracing()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