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/SkSize.h"
16 #include "ui/gfx/rect_conversions.h"
17 #include "ui/gfx/size_conversions.h"
18 #include "ui/gfx/skia_util.h"
22 PicturePileImpl::ClonesForDrawing::ClonesForDrawing(
23 const PicturePileImpl
* pile
, int num_threads
) {
24 for (int i
= 0; i
< num_threads
; i
++) {
25 scoped_refptr
<PicturePileImpl
> clone
=
26 PicturePileImpl::CreateCloneForDrawing(pile
, i
);
27 clones_
.push_back(clone
);
31 PicturePileImpl::ClonesForDrawing::~ClonesForDrawing() {
34 scoped_refptr
<PicturePileImpl
> PicturePileImpl::Create() {
35 return make_scoped_refptr(new PicturePileImpl
);
38 scoped_refptr
<PicturePileImpl
> PicturePileImpl::CreateFromOther(
39 const PicturePileBase
* other
) {
40 return make_scoped_refptr(new PicturePileImpl(other
));
43 scoped_refptr
<PicturePileImpl
> PicturePileImpl::CreateCloneForDrawing(
44 const PicturePileImpl
* other
, unsigned thread_index
) {
45 return make_scoped_refptr(new PicturePileImpl(other
, thread_index
));
48 PicturePileImpl::PicturePileImpl()
49 : clones_for_drawing_(ClonesForDrawing(this, 0)) {
52 PicturePileImpl::PicturePileImpl(const PicturePileBase
* other
)
53 : PicturePileBase(other
),
54 clones_for_drawing_(ClonesForDrawing(
55 this, RasterWorkerPool::GetNumRasterThreads())) {
58 PicturePileImpl::PicturePileImpl(
59 const PicturePileImpl
* other
, unsigned thread_index
)
60 : PicturePileBase(other
, thread_index
),
61 clones_for_drawing_(ClonesForDrawing(this, 0)) {
64 PicturePileImpl::~PicturePileImpl() {
67 PicturePileImpl
* PicturePileImpl::GetCloneForDrawingOnThread(
68 unsigned thread_index
) const {
69 CHECK_GT(clones_for_drawing_
.clones_
.size(), thread_index
);
70 return clones_for_drawing_
.clones_
[thread_index
].get();
73 void PicturePileImpl::RasterDirect(
75 const gfx::Rect
& canvas_rect
,
77 RenderingStatsInstrumentation
* rendering_stats_instrumentation
) {
82 rendering_stats_instrumentation
,
86 void PicturePileImpl::RasterForAnalysis(
87 skia::AnalysisCanvas
* canvas
,
88 const gfx::Rect
& canvas_rect
,
90 RenderingStatsInstrumentation
* stats_instrumentation
) {
92 canvas
, canvas
, canvas_rect
, contents_scale
, stats_instrumentation
, true);
95 void PicturePileImpl::RasterToBitmap(
97 const gfx::Rect
& canvas_rect
,
99 RenderingStatsInstrumentation
* rendering_stats_instrumentation
) {
100 if (clear_canvas_with_debug_color_
) {
101 // Any non-painted areas will be left in this color.
102 canvas
->clear(DebugColors::NonPaintedFillColor());
105 // If this picture has opaque contents, it is guaranteeing that it will
106 // draw an opaque rect the size of the layer. If it is not, then we must
107 // clear this canvas ourselves.
108 if (!contents_opaque_
) {
109 // Clearing is about ~4x faster than drawing a rect even if the content
110 // isn't covering a majority of the canvas.
111 canvas
->clear(SK_ColorTRANSPARENT
);
113 // Even if it is opaque, on any rasterizations that touch the edge of the
114 // layer, we also need to raster the background color underneath the last
115 // texel (since the recording won't cover it) and outside the last texel
116 // (due to linear filtering when using this texture).
117 gfx::SizeF total_content_size
= gfx::ScaleSize(tiling_
.total_size(),
119 gfx::Rect
content_rect(gfx::ToCeiledSize(total_content_size
));
121 // The final texel of content may only be partially covered by a
122 // rasterization; this rect represents the content rect that is fully
123 // covered by content.
124 gfx::Rect deflated_content_rect
= content_rect
;
125 deflated_content_rect
.Inset(0, 0, 1, 1);
126 if (!deflated_content_rect
.Contains(canvas_rect
)) {
127 // Drawing at most 2 x 2 x (canvas width + canvas height) texels is 2-3X
128 // faster than clearing, so special case this.
130 canvas
->translate(-canvas_rect
.x(), -canvas_rect
.y());
131 gfx::Rect inflated_content_rect
= content_rect
;
132 inflated_content_rect
.Inset(0, 0, -1, -1);
133 canvas
->clipRect(gfx::RectToSkRect(inflated_content_rect
),
134 SkRegion::kReplace_Op
);
135 canvas
->clipRect(gfx::RectToSkRect(deflated_content_rect
),
136 SkRegion::kDifference_Op
);
137 canvas
->drawColor(background_color_
, SkXfermode::kSrc_Mode
);
146 rendering_stats_instrumentation
,
150 void PicturePileImpl::CoalesceRasters(const gfx::Rect
& canvas_rect
,
151 const gfx::Rect
& content_rect
,
152 float contents_scale
,
153 PictureRegionMap
* results
) {
155 // Rasterize the collection of relevant picture piles.
156 gfx::Rect layer_rect
= gfx::ScaleToEnclosingRect(
157 content_rect
, 1.f
/ contents_scale
);
159 // Coalesce rasters of the same picture into different rects:
160 // - Compute the clip of each of the pile chunks,
161 // - Subtract it from the canvas rect to get difference region
162 // - Later, use the difference region to subtract each of the comprising
163 // rects from the canvas.
164 // Note that in essence, we're trying to mimic clipRegion with intersect op
165 // that also respects the current canvas transform and clip. In order to use
166 // the canvas transform, we must stick to clipRect operations (clipRegion
167 // ignores the transform). Intersect then can be written as subtracting the
168 // negation of the region we're trying to intersect. Luckily, we know that all
169 // of the rects will have to fit into |content_rect|, so we can start with
170 // that and subtract chunk rects to get the region that we need to subtract
171 // from the canvas. Then, we can use clipRect with difference op to subtract
172 // each rect in the region.
173 for (TilingData::Iterator
tile_iter(&tiling_
, layer_rect
);
174 tile_iter
; ++tile_iter
) {
175 PictureMap::iterator map_iter
= picture_map_
.find(tile_iter
.index());
176 if (map_iter
== picture_map_
.end())
178 PictureInfo
& info
= map_iter
->second
;
179 Picture
* picture
= info
.GetPicture();
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
);
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
);
204 Region
& region
= it
->second
;
205 region
.Subtract(content_clip
);
209 void PicturePileImpl::RasterCommon(
211 SkDrawPictureCallback
* callback
,
212 const gfx::Rect
& canvas_rect
,
213 float contents_scale
,
214 RenderingStatsInstrumentation
* rendering_stats_instrumentation
,
216 DCHECK(contents_scale
>= min_contents_scale_
);
218 canvas
->translate(-canvas_rect
.x(), -canvas_rect
.y());
219 gfx::SizeF total_content_size
= gfx::ScaleSize(tiling_
.total_size(),
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
);
225 canvas
->clipRect(gfx::RectToSkRect(content_rect
),
226 SkRegion::kIntersect_Op
);
228 PictureRegionMap picture_region_map
;
230 canvas_rect
, content_rect
, contents_scale
, &picture_region_map
);
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();
241 Picture
* picture
= it
->first
;
242 Region negated_clip_region
= it
->second
;
245 Region positive_clip
= content_rect
;
246 positive_clip
.Subtract(negated_clip_region
);
247 total_clip
.Union(positive_clip
);
250 base::TimeDelta best_duration
= base::TimeDelta::Max();
251 int repeat_count
= std::max(1, slow_down_raster_scale_factor_for_debug_
);
252 int rasterized_pixel_count
= 0;
254 for (int j
= 0; j
< repeat_count
; ++j
) {
255 base::TimeTicks start_time
;
256 if (rendering_stats_instrumentation
)
257 start_time
= rendering_stats_instrumentation
->StartRecording();
259 rasterized_pixel_count
= picture
->Raster(
260 canvas
, callback
, negated_clip_region
, contents_scale
);
262 if (rendering_stats_instrumentation
) {
263 base::TimeDelta duration
=
264 rendering_stats_instrumentation
->EndRecording(start_time
);
265 best_duration
= std::min(best_duration
, duration
);
269 if (rendering_stats_instrumentation
) {
271 rendering_stats_instrumentation
->AddAnalysis(best_duration
,
272 rasterized_pixel_count
);
274 rendering_stats_instrumentation
->AddRaster(best_duration
,
275 rasterized_pixel_count
);
281 // Fill the clip with debug color. This allows us to
282 // distinguish between non painted areas and problems with missing
285 for (Region::Iterator
it(total_clip
); it
.has_rect(); it
.next())
286 canvas
->clipRect(gfx::RectToSkRect(it
.rect()), SkRegion::kDifference_Op
);
287 paint
.setColor(DebugColors::MissingPictureFillColor());
288 paint
.setXfermodeMode(SkXfermode::kSrc_Mode
);
289 canvas
->drawPaint(paint
);
293 skia::RefPtr
<SkPicture
> PicturePileImpl::GetFlattenedPicture() {
294 TRACE_EVENT0("cc", "PicturePileImpl::GetFlattenedPicture");
296 gfx::Rect
layer_rect(tiling_
.total_size());
297 skia::RefPtr
<SkPicture
> picture
= skia::AdoptRef(new SkPicture
);
298 if (layer_rect
.IsEmpty())
301 SkCanvas
* canvas
= picture
->beginRecording(
304 SkPicture::kUsePathBoundsForClip_RecordingFlag
);
306 RasterToBitmap(canvas
, layer_rect
, 1.0, NULL
);
307 picture
->endRecording();
312 void PicturePileImpl::AnalyzeInRect(
313 const gfx::Rect
& content_rect
,
314 float contents_scale
,
315 PicturePileImpl::Analysis
* analysis
) {
316 AnalyzeInRect(content_rect
, contents_scale
, analysis
, NULL
);
319 void PicturePileImpl::AnalyzeInRect(
320 const gfx::Rect
& content_rect
,
321 float contents_scale
,
322 PicturePileImpl::Analysis
* analysis
,
323 RenderingStatsInstrumentation
* stats_instrumentation
) {
325 TRACE_EVENT0("cc", "PicturePileImpl::AnalyzeInRect");
327 gfx::Rect layer_rect
= gfx::ScaleToEnclosingRect(
328 content_rect
, 1.0f
/ contents_scale
);
330 layer_rect
.Intersect(gfx::Rect(tiling_
.total_size()));
332 skia::AnalysisCanvas
canvas(layer_rect
.width(), layer_rect
.height());
334 RasterForAnalysis(&canvas
, layer_rect
, 1.0f
, stats_instrumentation
);
336 analysis
->is_solid_color
= canvas
.GetColorIfSolid(&analysis
->solid_color
);
337 analysis
->has_text
= canvas
.HasText();
340 PicturePileImpl::Analysis::Analysis()
341 : is_solid_color(false),
345 PicturePileImpl::Analysis::~Analysis() {
348 PicturePileImpl::PixelRefIterator::PixelRefIterator(
349 const gfx::Rect
& content_rect
,
350 float contents_scale
,
351 const PicturePileImpl
* picture_pile
)
352 : picture_pile_(picture_pile
),
353 layer_rect_(gfx::ScaleToEnclosingRect(
354 content_rect
, 1.f
/ contents_scale
)),
355 tile_iterator_(&picture_pile_
->tiling_
, layer_rect_
) {
356 // Early out if there isn't a single tile.
360 AdvanceToTilePictureWithPixelRefs();
363 PicturePileImpl::PixelRefIterator::~PixelRefIterator() {
366 PicturePileImpl::PixelRefIterator
&
367 PicturePileImpl::PixelRefIterator::operator++() {
368 ++pixel_ref_iterator_
;
369 if (pixel_ref_iterator_
)
373 AdvanceToTilePictureWithPixelRefs();
377 void PicturePileImpl::PixelRefIterator::AdvanceToTilePictureWithPixelRefs() {
378 for (; tile_iterator_
; ++tile_iterator_
) {
379 PictureMap::const_iterator it
=
380 picture_pile_
->picture_map_
.find(tile_iterator_
.index());
381 if (it
== picture_pile_
->picture_map_
.end())
384 const Picture
* picture
= it
->second
.GetPicture();
385 if (!picture
|| (processed_pictures_
.count(picture
) != 0) ||
386 !picture
->WillPlayBackBitmaps())
389 processed_pictures_
.insert(picture
);
390 pixel_ref_iterator_
= Picture::PixelRefIterator(layer_rect_
, picture
);
391 if (pixel_ref_iterator_
)
396 void PicturePileImpl::DidBeginTracing() {
397 gfx::Rect
layer_rect(tiling_
.total_size());
398 std::set
<void*> processed_pictures
;
399 for (PictureMap::iterator it
= picture_map_
.begin();
400 it
!= picture_map_
.end();
402 Picture
* picture
= it
->second
.GetPicture();
403 if (picture
&& (processed_pictures
.count(picture
) == 0)) {
404 picture
->EmitTraceSnapshot();
405 processed_pictures
.insert(picture
);