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