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.
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"
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(
76 const gfx::Rect
& canvas_rect
,
78 RenderingStatsInstrumentation
* rendering_stats_instrumentation
) {
83 rendering_stats_instrumentation
,
87 void PicturePileImpl::RasterForAnalysis(
88 skia::AnalysisCanvas
* canvas
,
89 const gfx::Rect
& canvas_rect
,
91 RenderingStatsInstrumentation
* stats_instrumentation
) {
93 canvas
, canvas
, canvas_rect
, contents_scale
, stats_instrumentation
, true);
96 void PicturePileImpl::RasterToBitmap(
98 const gfx::Rect
& canvas_rect
,
100 RenderingStatsInstrumentation
* rendering_stats_instrumentation
) {
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.
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
);
138 // Drawing at most 2 x 2 x (canvas width + canvas height) texels is 2-3X
139 // faster than clearing, so special case this.
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
);
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
);
162 rendering_stats_instrumentation
,
166 void PicturePileImpl::CoalesceRasters(const gfx::Rect
& canvas_rect
,
167 const gfx::Rect
& content_rect
,
168 float contents_scale
,
169 PictureRegionMap
* 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
);
200 PictureMap::iterator map_iter
= picture_map_
.find(tile_iter
.index());
201 if (map_iter
== picture_map_
.end())
203 PictureInfo
& info
= map_iter
->second
;
204 Picture
* picture
= info
.GetPicture();
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();
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
);
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
;
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(
263 SkDrawPictureCallback
* callback
,
264 const gfx::Rect
& canvas_rect
,
265 float contents_scale
,
266 RenderingStatsInstrumentation
* rendering_stats_instrumentation
,
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
;
280 canvas_rect
, content_tiling_rect
, contents_scale
, &picture_region_map
);
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();
291 Picture
* picture
= it
->first
;
292 Region negated_clip_region
= it
->second
;
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
);
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
) {
323 rendering_stats_instrumentation
->AddAnalysis(best_duration
,
324 rasterized_pixel_count
);
326 rendering_stats_instrumentation
->AddRaster(best_duration
,
327 rasterized_pixel_count
);
333 // Fill the clip with debug color. This allows us to
334 // distinguish between non painted areas and problems with missing
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
);
345 skia::RefPtr
<SkPicture
> PicturePileImpl::GetFlattenedPicture() {
346 TRACE_EVENT0("cc", "PicturePileImpl::GetFlattenedPicture");
348 gfx::Rect
tiling_rect(tiling_
.tiling_rect());
349 SkPictureRecorder recorder
;
351 recorder
.beginRecording(tiling_rect
.width(),
352 tiling_rect
.height(),
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());
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
) {
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
),
405 gfx::ScaleToEnclosingRect(content_rect
, 1.f
/ contents_scale
)),
406 tile_iterator_(&picture_pile_
->tiling_
,
408 false /* include_borders */) {
409 // Early out if there isn't a single tile.
413 AdvanceToTilePictureWithPixelRefs();
416 PicturePileImpl::PixelRefIterator::~PixelRefIterator() {
419 PicturePileImpl::PixelRefIterator
&
420 PicturePileImpl::PixelRefIterator::operator++() {
421 ++pixel_ref_iterator_
;
422 if (pixel_ref_iterator_
)
426 AdvanceToTilePictureWithPixelRefs();
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())
437 const Picture
* picture
= it
->second
.GetPicture();
438 if (!picture
|| (processed_pictures_
.count(picture
) != 0) ||
439 !picture
->WillPlayBackBitmaps())
442 processed_pictures_
.insert(picture
);
443 pixel_ref_iterator_
= Picture::PixelRefIterator(layer_rect_
, picture
);
444 if (pixel_ref_iterator_
)
449 void PicturePileImpl::DidBeginTracing() {
450 std::set
<void*> processed_pictures
;
451 for (PictureMap::iterator it
= picture_map_
.begin();
452 it
!= picture_map_
.end();
454 Picture
* picture
= it
->second
.GetPicture();
455 if (picture
&& (processed_pictures
.count(picture
) == 0)) {
456 picture
->EmitTraceSnapshot();
457 processed_pictures
.insert(picture
);