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/benchmark_instrumentation.h"
11 #include "cc/debug/debug_colors.h"
12 #include "cc/resources/picture_pile_impl.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(this, num_raster_threads())) {
57 PicturePileImpl::PicturePileImpl(
58 const PicturePileImpl
* other
, unsigned thread_index
)
59 : PicturePileBase(other
, thread_index
),
60 clones_for_drawing_(ClonesForDrawing(this, 0)) {
63 PicturePileImpl::~PicturePileImpl() {
66 PicturePileImpl
* PicturePileImpl::GetCloneForDrawingOnThread(
67 unsigned thread_index
) const {
68 CHECK_GT(clones_for_drawing_
.clones_
.size(), thread_index
);
69 return clones_for_drawing_
.clones_
[thread_index
].get();
72 void PicturePileImpl::RasterDirect(
74 gfx::Rect canvas_rect
,
76 RasterStats
* raster_stats
) {
77 RasterCommon(canvas
, NULL
, canvas_rect
, contents_scale
, raster_stats
);
80 void PicturePileImpl::RasterForAnalysis(
81 skia::AnalysisCanvas
* canvas
,
82 gfx::Rect canvas_rect
,
83 float contents_scale
) {
84 RasterCommon(canvas
, canvas
, canvas_rect
, contents_scale
, NULL
);
87 void PicturePileImpl::RasterToBitmap(
89 gfx::Rect canvas_rect
,
91 RasterStats
* raster_stats
) {
93 // Any non-painted areas will be left in this color.
94 canvas
->clear(DebugColors::NonPaintedFillColor());
97 // If this picture has opaque contents, it is guaranteeing that it will
98 // draw an opaque rect the size of the layer. If it is not, then we must
99 // clear this canvas ourselves.
100 if (!contents_opaque_
) {
101 // Clearing is about ~4x faster than drawing a rect even if the content
102 // isn't covering a majority of the canvas.
103 canvas
->clear(SK_ColorTRANSPARENT
);
105 // Even if it is opaque, on any rasterizations that touch the edge of the
106 // layer, we also need to raster the background color underneath the last
107 // texel (since the recording won't cover it) and outside the last texel
108 // (due to linear filtering when using this texture).
109 gfx::SizeF total_content_size
= gfx::ScaleSize(tiling_
.total_size(),
111 gfx::Rect
content_rect(gfx::ToCeiledSize(total_content_size
));
112 gfx::Rect deflated_content_rect
= content_rect
;
113 content_rect
.Intersect(canvas_rect
);
115 // The final texel of content may only be partially covered by a
116 // rasterization; this rect represents the content rect that is fully
117 // covered by content.
118 deflated_content_rect
.Inset(0, 0, 1, 1);
119 deflated_content_rect
.Intersect(canvas_rect
);
120 if (!deflated_content_rect
.Contains(canvas_rect
)) {
121 // Drawing at most 2 x 2 x (canvas width + canvas height) texels is 2-3X
122 // faster than clearing, so special case this.
124 gfx::Rect inflated_content_rect
= content_rect
;
125 inflated_content_rect
.Inset(0, 0, -1, -1);
126 canvas
->clipRect(gfx::RectToSkRect(inflated_content_rect
),
127 SkRegion::kReplace_Op
);
128 canvas
->clipRect(gfx::RectToSkRect(deflated_content_rect
),
129 SkRegion::kDifference_Op
);
130 canvas
->drawColor(background_color_
, SkXfermode::kSrc_Mode
);
135 RasterCommon(canvas
, NULL
, canvas_rect
, contents_scale
, raster_stats
);
138 void PicturePileImpl::RasterCommon(
140 SkDrawPictureCallback
* callback
,
141 gfx::Rect canvas_rect
,
142 float contents_scale
,
143 RasterStats
* raster_stats
) {
144 DCHECK(contents_scale
>= min_contents_scale_
);
146 canvas
->translate(-canvas_rect
.x(), -canvas_rect
.y());
148 gfx::SizeF total_content_size
= gfx::ScaleSize(tiling_
.total_size(),
150 gfx::Rect
total_content_rect(gfx::ToCeiledSize(total_content_size
));
151 gfx::Rect content_rect
= total_content_rect
;
152 content_rect
.Intersect(canvas_rect
);
154 // Rasterize the collection of relevant picture piles.
155 gfx::Rect layer_rect
= gfx::ScaleToEnclosingRect(
156 content_rect
, 1.f
/ contents_scale
);
158 canvas
->clipRect(gfx::RectToSkRect(content_rect
),
159 SkRegion::kIntersect_Op
);
160 Region
unclipped(content_rect
);
163 raster_stats
->total_pixels_rasterized
= 0;
164 raster_stats
->total_rasterize_time
= base::TimeDelta::FromSeconds(0);
165 raster_stats
->best_rasterize_time
= base::TimeDelta::FromSeconds(0);
168 for (TilingData::Iterator
tile_iter(&tiling_
, layer_rect
);
169 tile_iter
; ++tile_iter
) {
170 PictureListMap::iterator map_iter
=
171 picture_list_map_
.find(tile_iter
.index());
172 if (map_iter
== picture_list_map_
.end())
174 PictureList
& pic_list
= map_iter
->second
;
175 if (pic_list
.empty())
178 // Raster through the picture list top down, using clips to make sure that
179 // pictures on top are not overdrawn by pictures on the bottom.
180 for (PictureList::reverse_iterator i
= pic_list
.rbegin();
181 i
!= pic_list
.rend(); ++i
) {
182 // This is intentionally *enclosed* rect, so that the clip is aligned on
183 // integral post-scale content pixels and does not extend past the edges
184 // of the picture's layer rect. The min_contents_scale enforces that
185 // enough buffer pixels have been added such that the enclosed rect
186 // encompasses all invalidated pixels at any larger scale level.
187 gfx::Rect content_clip
= gfx::ScaleToEnclosedRect(
188 (*i
)->LayerRect(), contents_scale
);
190 DCHECK(!content_clip
.IsEmpty()) <<
191 "Layer rect: " << (*i
)->LayerRect().ToString() <<
192 "Contents scale: " << contents_scale
;
194 content_clip
.Intersect(canvas_rect
);
196 if (!unclipped
.Intersects(content_clip
))
199 base::TimeDelta total_duration
=
200 base::TimeDelta::FromInternalValue(0);
201 base::TimeDelta best_duration
=
202 base::TimeDelta::FromInternalValue(std::numeric_limits
<int64
>::max());
203 int repeat_count
= std::max(1, slow_down_raster_scale_factor_for_debug_
);
205 TRACE_EVENT0(benchmark_instrumentation::kCategory
,
206 benchmark_instrumentation::kRasterLoop
);
207 for (int j
= 0; j
< repeat_count
; ++j
) {
208 base::TimeTicks start_time
;
210 start_time
= base::TimeTicks::HighResNow();
212 (*i
)->Raster(canvas
, callback
, content_clip
, contents_scale
);
215 base::TimeDelta duration
= base::TimeTicks::HighResNow() - start_time
;
216 total_duration
+= duration
;
217 best_duration
= std::min(best_duration
, duration
);
222 raster_stats
->total_pixels_rasterized
+=
223 repeat_count
* content_clip
.width() * content_clip
.height();
224 raster_stats
->total_rasterize_time
+= total_duration
;
225 raster_stats
->best_rasterize_time
+= best_duration
;
228 if (show_debug_picture_borders_
) {
229 gfx::Rect border
= gfx::ScaleToEnclosedRect(
230 (*i
)->LayerRect(), contents_scale
);
231 border
.Inset(0, 0, 1, 1);
233 SkPaint picture_border_paint
;
234 picture_border_paint
.setColor(DebugColors::PictureBorderColor());
235 canvas
->drawLine(border
.x(), border
.y(), border
.right(), border
.y(),
236 picture_border_paint
);
237 canvas
->drawLine(border
.right(), border
.y(), border
.right(),
238 border
.bottom(), picture_border_paint
);
239 canvas
->drawLine(border
.right(), border
.bottom(), border
.x(),
240 border
.bottom(), picture_border_paint
);
241 canvas
->drawLine(border
.x(), border
.bottom(), border
.x(), border
.y(),
242 picture_border_paint
);
245 // Don't allow pictures underneath to draw where this picture did.
247 gfx::RectToSkRect(content_clip
),
248 SkRegion::kDifference_Op
);
249 unclipped
.Subtract(content_clip
);
254 // Fill the remaining clip with debug color. This allows us to
255 // distinguish between non painted areas and problems with missing
258 paint
.setColor(DebugColors::MissingPictureFillColor());
259 paint
.setXfermodeMode(SkXfermode::kSrc_Mode
);
260 canvas
->drawPaint(paint
);
263 // We should always paint some part of |content_rect|.
264 DCHECK(!unclipped
.Contains(content_rect
));
267 skia::RefPtr
<SkPicture
> PicturePileImpl::GetFlattenedPicture() {
268 TRACE_EVENT0("cc", "PicturePileImpl::GetFlattenedPicture");
270 gfx::Rect
layer_rect(tiling_
.total_size());
271 skia::RefPtr
<SkPicture
> picture
= skia::AdoptRef(new SkPicture
);
272 if (layer_rect
.IsEmpty())
275 SkCanvas
* canvas
= picture
->beginRecording(
278 SkPicture::kUsePathBoundsForClip_RecordingFlag
);
280 RasterToBitmap(canvas
, layer_rect
, 1.0, NULL
);
281 picture
->endRecording();
286 void PicturePileImpl::AnalyzeInRect(gfx::Rect content_rect
,
287 float contents_scale
,
288 PicturePileImpl::Analysis
* analysis
) {
290 TRACE_EVENT0("cc", "PicturePileImpl::AnalyzeInRect");
292 gfx::Rect layer_rect
= gfx::ScaleToEnclosingRect(
293 content_rect
, 1.0f
/ contents_scale
);
295 layer_rect
.Intersect(gfx::Rect(tiling_
.total_size()));
297 SkBitmap empty_bitmap
;
298 empty_bitmap
.setConfig(SkBitmap::kNo_Config
,
300 layer_rect
.height());
301 skia::AnalysisDevice
device(empty_bitmap
);
302 skia::AnalysisCanvas
canvas(&device
);
304 RasterForAnalysis(&canvas
, layer_rect
, 1.0f
);
306 analysis
->is_solid_color
= canvas
.GetColorIfSolid(&analysis
->solid_color
);
307 analysis
->has_text
= canvas
.HasText();
310 PicturePileImpl::Analysis::Analysis()
311 : is_solid_color(false),
315 PicturePileImpl::Analysis::~Analysis() {
318 PicturePileImpl::PixelRefIterator::PixelRefIterator(
319 gfx::Rect content_rect
,
320 float contents_scale
,
321 const PicturePileImpl
* picture_pile
)
322 : picture_pile_(picture_pile
),
323 layer_rect_(gfx::ScaleToEnclosingRect(
324 content_rect
, 1.f
/ contents_scale
)),
325 tile_iterator_(&picture_pile_
->tiling_
, layer_rect_
),
326 picture_list_(NULL
) {
327 // Early out if there isn't a single tile.
331 if (AdvanceToTileWithPictures())
332 AdvanceToPictureWithPixelRefs();
335 PicturePileImpl::PixelRefIterator::~PixelRefIterator() {
338 PicturePileImpl::PixelRefIterator
&
339 PicturePileImpl::PixelRefIterator::operator++() {
340 ++pixel_ref_iterator_
;
341 if (pixel_ref_iterator_
)
344 ++picture_list_iterator_
;
345 AdvanceToPictureWithPixelRefs();
349 bool PicturePileImpl::PixelRefIterator::AdvanceToTileWithPictures() {
350 for (; tile_iterator_
; ++tile_iterator_
) {
351 PictureListMap::const_iterator map_iterator
=
352 picture_pile_
->picture_list_map_
.find(tile_iterator_
.index());
353 if (map_iterator
!= picture_pile_
->picture_list_map_
.end()) {
354 picture_list_
= &map_iterator
->second
;
355 picture_list_iterator_
= picture_list_
->begin();
363 void PicturePileImpl::PixelRefIterator::AdvanceToPictureWithPixelRefs() {
364 DCHECK(tile_iterator_
);
367 picture_list_iterator_
!= picture_list_
->end();
368 ++picture_list_iterator_
) {
369 pixel_ref_iterator_
=
370 Picture::PixelRefIterator(layer_rect_
, picture_list_iterator_
->get());
371 if (pixel_ref_iterator_
)
375 } while (AdvanceToTileWithPictures());
378 void PicturePileImpl::DidBeginTracing() {
379 gfx::Rect
layer_rect(tiling_
.total_size());
380 for (PictureListMap::iterator pli
= picture_list_map_
.begin();
381 pli
!= picture_list_map_
.end();
383 PictureList
& picture_list
= (*pli
).second
;
384 for (PictureList::iterator picture
= picture_list
.begin();
385 picture
!= picture_list
.end();
387 (*picture
)->EmitTraceSnapshot();