Temporarily re-enabling SizeAfterPrefChange test with traces.
[chromium-blink-merge.git] / cc / resources / picture_pile_impl.cc
blobbb467474f8dc3ba99bf5980c41cb38edddacbb80
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.
5 #include <algorithm>
6 #include <limits>
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"
21 namespace cc {
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);
32 PicturePileImpl::ClonesForDrawing::~ClonesForDrawing() {
35 scoped_refptr<PicturePileImpl> PicturePileImpl::Create() {
36 return make_scoped_refptr(new PicturePileImpl);
39 scoped_refptr<PicturePileImpl> PicturePileImpl::CreateFromOther(
40 const PicturePileBase* other) {
41 return make_scoped_refptr(new PicturePileImpl(other));
44 scoped_refptr<PicturePileImpl> PicturePileImpl::CreateCloneForDrawing(
45 const PicturePileImpl* other, unsigned thread_index) {
46 return make_scoped_refptr(new PicturePileImpl(other, thread_index));
49 PicturePileImpl::PicturePileImpl()
50 : clones_for_drawing_(ClonesForDrawing(this, 0)) {
53 PicturePileImpl::PicturePileImpl(const PicturePileBase* other)
54 : PicturePileBase(other),
55 clones_for_drawing_(ClonesForDrawing(
56 this, RasterWorkerPool::GetNumRasterThreads())) {
59 PicturePileImpl::PicturePileImpl(
60 const PicturePileImpl* other, unsigned thread_index)
61 : PicturePileBase(other, thread_index),
62 clones_for_drawing_(ClonesForDrawing(this, 0)) {
65 PicturePileImpl::~PicturePileImpl() {
68 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();
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);
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);
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());
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));
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();
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();
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);
158 RasterCommon(canvas,
159 NULL,
160 canvas_rect,
161 contents_scale,
162 rendering_stats_instrumentation,
163 false);
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);
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;
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;
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);
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();
234 last_col_index = tile_iter.index_x();
235 last_row_index = tile_iter.index_y();
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);
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;
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;
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_);
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);
275 canvas->clipRect(gfx::RectToSkRect(content_tiling_rect),
276 SkRegion::kIntersect_Op);
278 PictureRegionMap picture_region_map;
279 CoalesceRasters(
280 canvas_rect, content_tiling_rect, contents_scale, &picture_region_map);
282 #ifndef NDEBUG
283 Region total_clip;
284 #endif // NDEBUG
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;
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
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;
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();
311 rasterized_pixel_count = picture->Raster(
312 canvas, callback, negated_clip_region, contents_scale);
314 if (rendering_stats_instrumentation) {
315 base::TimeDelta duration =
316 rendering_stats_instrumentation->EndRecording(start_time);
317 best_duration = std::min(best_duration, duration);
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);
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
345 skia::RefPtr<SkPicture> PicturePileImpl::GetFlattenedPicture() {
346 TRACE_EVENT0("cc", "PicturePileImpl::GetFlattenedPicture");
348 gfx::Rect tiling_rect(tiling_.tiling_rect());
349 SkPictureRecorder recorder;
350 SkCanvas* canvas =
351 recorder.beginRecording(tiling_rect.width(),
352 tiling_rect.height(),
353 NULL,
354 SkPicture::kUsePathBoundsForClip_RecordingFlag);
355 if (!tiling_rect.IsEmpty())
356 RasterToBitmap(canvas, tiling_rect, 1.0, NULL);
357 skia::RefPtr<SkPicture> picture = skia::AdoptRef(recorder.endRecording());
359 return picture;
362 void PicturePileImpl::AnalyzeInRect(
363 const gfx::Rect& content_rect,
364 float contents_scale,
365 PicturePileImpl::Analysis* analysis) {
366 AnalyzeInRect(content_rect, contents_scale, analysis, NULL);
369 void PicturePileImpl::AnalyzeInRect(
370 const gfx::Rect& content_rect,
371 float contents_scale,
372 PicturePileImpl::Analysis* analysis,
373 RenderingStatsInstrumentation* stats_instrumentation) {
374 DCHECK(analysis);
375 TRACE_EVENT0("cc", "PicturePileImpl::AnalyzeInRect");
377 gfx::Rect layer_rect = gfx::ScaleToEnclosingRect(
378 content_rect, 1.0f / contents_scale);
380 layer_rect.Intersect(tiling_.tiling_rect());
382 skia::AnalysisCanvas canvas(layer_rect.width(), layer_rect.height());
384 RasterForAnalysis(&canvas, layer_rect, 1.0f, stats_instrumentation);
386 analysis->is_solid_color = canvas.GetColorIfSolid(&analysis->solid_color);
387 analysis->has_text = canvas.HasText();
390 // Since there are situations when we can skip analysis, the variables have to
391 // be set to their safest values. That is, we have to assume that the tile is
392 // not solid color. As well, we have to assume that the tile has text so we
393 // don't early out incorrectly.
394 PicturePileImpl::Analysis::Analysis() : is_solid_color(false), has_text(true) {}
396 PicturePileImpl::Analysis::~Analysis() {
399 PicturePileImpl::PixelRefIterator::PixelRefIterator(
400 const gfx::Rect& content_rect,
401 float contents_scale,
402 const PicturePileImpl* picture_pile)
403 : picture_pile_(picture_pile),
404 layer_rect_(
405 gfx::ScaleToEnclosingRect(content_rect, 1.f / contents_scale)),
406 tile_iterator_(&picture_pile_->tiling_,
407 layer_rect_,
408 false /* include_borders */) {
409 // Early out if there isn't a single tile.
410 if (!tile_iterator_)
411 return;
413 AdvanceToTilePictureWithPixelRefs();
416 PicturePileImpl::PixelRefIterator::~PixelRefIterator() {
419 PicturePileImpl::PixelRefIterator&
420 PicturePileImpl::PixelRefIterator::operator++() {
421 ++pixel_ref_iterator_;
422 if (pixel_ref_iterator_)
423 return *this;
425 ++tile_iterator_;
426 AdvanceToTilePictureWithPixelRefs();
427 return *this;
430 void PicturePileImpl::PixelRefIterator::AdvanceToTilePictureWithPixelRefs() {
431 for (; tile_iterator_; ++tile_iterator_) {
432 PictureMap::const_iterator it =
433 picture_pile_->picture_map_.find(tile_iterator_.index());
434 if (it == picture_pile_->picture_map_.end())
435 continue;
437 const Picture* picture = it->second.GetPicture();
438 if (!picture || (processed_pictures_.count(picture) != 0) ||
439 !picture->WillPlayBackBitmaps())
440 continue;
442 processed_pictures_.insert(picture);
443 pixel_ref_iterator_ = Picture::PixelRefIterator(layer_rect_, picture);
444 if (pixel_ref_iterator_)
445 break;
449 void PicturePileImpl::DidBeginTracing() {
450 std::set<void*> processed_pictures;
451 for (PictureMap::iterator it = picture_map_.begin();
452 it != picture_map_.end();
453 ++it) {
454 Picture* picture = it->second.GetPicture();
455 if (picture && (processed_pictures.count(picture) == 0)) {
456 picture->EmitTraceSnapshot();
457 processed_pictures.insert(picture);
462 } // namespace cc