• 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 "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 
ClonesForDrawing(const PicturePileImpl * pile,int num_threads)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 
~ClonesForDrawing()30 PicturePileImpl::ClonesForDrawing::~ClonesForDrawing() {
31 }
32 
Create()33 scoped_refptr<PicturePileImpl> PicturePileImpl::Create() {
34   return make_scoped_refptr(new PicturePileImpl);
35 }
36 
CreateFromOther(const PicturePileBase * other)37 scoped_refptr<PicturePileImpl> PicturePileImpl::CreateFromOther(
38     const PicturePileBase* other) {
39   return make_scoped_refptr(new PicturePileImpl(other));
40 }
41 
CreateCloneForDrawing(const PicturePileImpl * other,unsigned thread_index)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 
PicturePileImpl()47 PicturePileImpl::PicturePileImpl()
48     : clones_for_drawing_(ClonesForDrawing(this, 0)) {
49 }
50 
PicturePileImpl(const PicturePileBase * other)51 PicturePileImpl::PicturePileImpl(const PicturePileBase* other)
52     : PicturePileBase(other),
53       clones_for_drawing_(ClonesForDrawing(this, num_raster_threads())) {
54 }
55 
PicturePileImpl(const PicturePileImpl * other,unsigned thread_index)56 PicturePileImpl::PicturePileImpl(
57     const PicturePileImpl* other, unsigned thread_index)
58     : PicturePileBase(other, thread_index),
59       clones_for_drawing_(ClonesForDrawing(this, 0)) {
60 }
61 
~PicturePileImpl()62 PicturePileImpl::~PicturePileImpl() {
63 }
64 
GetCloneForDrawingOnThread(unsigned thread_index) const65 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 
RasterDirect(SkCanvas * canvas,gfx::Rect canvas_rect,float contents_scale,RenderingStatsInstrumentation * rendering_stats_instrumentation)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 
RasterForAnalysis(skia::AnalysisCanvas * canvas,gfx::Rect canvas_rect,float contents_scale,RenderingStatsInstrumentation * stats_instrumentation)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 
RasterToBitmap(SkCanvas * canvas,gfx::Rect canvas_rect,float contents_scale,RenderingStatsInstrumentation * rendering_stats_instrumentation)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 
CoalesceRasters(gfx::Rect canvas_rect,gfx::Rect content_rect,float contents_scale,PictureRegionMap * results)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 
RasterCommon(SkCanvas * canvas,SkDrawPictureCallback * callback,gfx::Rect canvas_rect,float contents_scale,RenderingStatsInstrumentation * rendering_stats_instrumentation,bool is_analysis)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 
GetFlattenedPicture()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 
AnalyzeInRect(gfx::Rect content_rect,float contents_scale,PicturePileImpl::Analysis * analysis)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 
AnalyzeInRect(gfx::Rect content_rect,float contents_scale,PicturePileImpl::Analysis * analysis,RenderingStatsInstrumentation * stats_instrumentation)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 
Analysis()346 PicturePileImpl::Analysis::Analysis()
347     : is_solid_color(false),
348       has_text(false) {
349 }
350 
~Analysis()351 PicturePileImpl::Analysis::~Analysis() {
352 }
353 
PixelRefIterator(gfx::Rect content_rect,float contents_scale,const PicturePileImpl * picture_pile)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 
~PixelRefIterator()371 PicturePileImpl::PixelRefIterator::~PixelRefIterator() {
372 }
373 
374 PicturePileImpl::PixelRefIterator&
operator ++()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 
AdvanceToTilePictureWithPixelRefs()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 
DidBeginTracing()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