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/SkSize.h"
15 #include "ui/gfx/rect_conversions.h"
16 #include "ui/gfx/size_conversions.h"
17 #include "ui/gfx/skia_util.h"
18
19 namespace cc {
20
ClonesForDrawing(const PicturePileImpl * pile,int num_threads)21 PicturePileImpl::ClonesForDrawing::ClonesForDrawing(
22 const PicturePileImpl* pile, int num_threads) {
23 for (int i = 0; i < num_threads; i++) {
24 scoped_refptr<PicturePileImpl> clone =
25 PicturePileImpl::CreateCloneForDrawing(pile, i);
26 clones_.push_back(clone);
27 }
28 }
29
~ClonesForDrawing()30 PicturePileImpl::ClonesForDrawing::~ClonesForDrawing() {
31 }
32
Create()33 scoped_refptr<PicturePileImpl> PicturePileImpl::Create() {
34 return make_scoped_refptr(new PicturePileImpl);
35 }
36
CreateFromOther(const PicturePileBase * other)37 scoped_refptr<PicturePileImpl> PicturePileImpl::CreateFromOther(
38 const PicturePileBase* other) {
39 return make_scoped_refptr(new PicturePileImpl(other));
40 }
41
CreateCloneForDrawing(const PicturePileImpl * other,unsigned thread_index)42 scoped_refptr<PicturePileImpl> PicturePileImpl::CreateCloneForDrawing(
43 const PicturePileImpl* other, unsigned thread_index) {
44 return make_scoped_refptr(new PicturePileImpl(other, thread_index));
45 }
46
PicturePileImpl()47 PicturePileImpl::PicturePileImpl()
48 : clones_for_drawing_(ClonesForDrawing(this, 0)) {
49 }
50
PicturePileImpl(const PicturePileBase * other)51 PicturePileImpl::PicturePileImpl(const PicturePileBase* other)
52 : PicturePileBase(other),
53 clones_for_drawing_(ClonesForDrawing(this, num_raster_threads())) {
54 }
55
PicturePileImpl(const PicturePileImpl * other,unsigned thread_index)56 PicturePileImpl::PicturePileImpl(
57 const PicturePileImpl* other, unsigned thread_index)
58 : PicturePileBase(other, thread_index),
59 clones_for_drawing_(ClonesForDrawing(this, 0)) {
60 }
61
~PicturePileImpl()62 PicturePileImpl::~PicturePileImpl() {
63 }
64
GetCloneForDrawingOnThread(unsigned thread_index) const65 PicturePileImpl* PicturePileImpl::GetCloneForDrawingOnThread(
66 unsigned thread_index) const {
67 CHECK_GT(clones_for_drawing_.clones_.size(), thread_index);
68 return clones_for_drawing_.clones_[thread_index].get();
69 }
70
RasterDirect(SkCanvas * canvas,gfx::Rect canvas_rect,float contents_scale,RenderingStatsInstrumentation * rendering_stats_instrumentation)71 void PicturePileImpl::RasterDirect(
72 SkCanvas* canvas,
73 gfx::Rect canvas_rect,
74 float contents_scale,
75 RenderingStatsInstrumentation* rendering_stats_instrumentation) {
76 RasterCommon(canvas,
77 NULL,
78 canvas_rect,
79 contents_scale,
80 rendering_stats_instrumentation,
81 false);
82 }
83
RasterForAnalysis(skia::AnalysisCanvas * canvas,gfx::Rect canvas_rect,float contents_scale,RenderingStatsInstrumentation * stats_instrumentation)84 void PicturePileImpl::RasterForAnalysis(
85 skia::AnalysisCanvas* canvas,
86 gfx::Rect canvas_rect,
87 float contents_scale,
88 RenderingStatsInstrumentation* stats_instrumentation) {
89 RasterCommon(
90 canvas, canvas, canvas_rect, contents_scale, stats_instrumentation, true);
91 }
92
RasterToBitmap(SkCanvas * canvas,gfx::Rect canvas_rect,float contents_scale,RenderingStatsInstrumentation * rendering_stats_instrumentation)93 void PicturePileImpl::RasterToBitmap(
94 SkCanvas* canvas,
95 gfx::Rect canvas_rect,
96 float contents_scale,
97 RenderingStatsInstrumentation* rendering_stats_instrumentation) {
98 if (clear_canvas_with_debug_color_) {
99 // Any non-painted areas will be left in this color.
100 canvas->clear(DebugColors::NonPaintedFillColor());
101 }
102
103 // If this picture has opaque contents, it is guaranteeing that it will
104 // draw an opaque rect the size of the layer. If it is not, then we must
105 // clear this canvas ourselves.
106 if (!contents_opaque_) {
107 // Clearing is about ~4x faster than drawing a rect even if the content
108 // isn't covering a majority of the canvas.
109 canvas->clear(SK_ColorTRANSPARENT);
110 } else {
111 // Even if it is opaque, on any 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::SizeF total_content_size = gfx::ScaleSize(tiling_.total_size(),
116 contents_scale);
117 gfx::Rect content_rect(gfx::ToCeiledSize(total_content_size));
118
119 // The final texel of content may only be partially covered by a
120 // rasterization; this rect represents the content rect that is fully
121 // covered by content.
122 gfx::Rect deflated_content_rect = content_rect;
123 deflated_content_rect.Inset(0, 0, 1, 1);
124 if (!deflated_content_rect.Contains(canvas_rect)) {
125 // Drawing at most 2 x 2 x (canvas width + canvas height) texels is 2-3X
126 // faster than clearing, so special case this.
127 canvas->save();
128 canvas->translate(-canvas_rect.x(), -canvas_rect.y());
129 gfx::Rect inflated_content_rect = content_rect;
130 inflated_content_rect.Inset(0, 0, -1, -1);
131 canvas->clipRect(gfx::RectToSkRect(inflated_content_rect),
132 SkRegion::kReplace_Op);
133 canvas->clipRect(gfx::RectToSkRect(deflated_content_rect),
134 SkRegion::kDifference_Op);
135 canvas->drawColor(background_color_, SkXfermode::kSrc_Mode);
136 canvas->restore();
137 }
138 }
139
140 RasterCommon(canvas,
141 NULL,
142 canvas_rect,
143 contents_scale,
144 rendering_stats_instrumentation,
145 false);
146 }
147
CoalesceRasters(gfx::Rect canvas_rect,gfx::Rect content_rect,float contents_scale,PictureRegionMap * results)148 void PicturePileImpl::CoalesceRasters(gfx::Rect canvas_rect,
149 gfx::Rect content_rect,
150 float contents_scale,
151 PictureRegionMap* results) {
152 DCHECK(results);
153 // Rasterize the collection of relevant picture piles.
154 gfx::Rect layer_rect = gfx::ScaleToEnclosingRect(
155 content_rect, 1.f / contents_scale);
156
157 // Coalesce rasters of the same picture into different rects:
158 // - Compute the clip of each of the pile chunks,
159 // - Subtract it from the canvas rect to get difference region
160 // - Later, use the difference region to subtract each of the comprising
161 // rects from the canvas.
162 // Note that in essence, we're trying to mimic clipRegion with intersect op
163 // that also respects the current canvas transform and clip. In order to use
164 // the canvas transform, we must stick to clipRect operations (clipRegion
165 // ignores the transform). Intersect then can be written as subtracting the
166 // negation of the region we're trying to intersect. Luckily, we know that all
167 // of the rects will have to fit into |content_rect|, so we can start with
168 // that and subtract chunk rects to get the region that we need to subtract
169 // from the canvas. Then, we can use clipRect with difference op to subtract
170 // each rect in the region.
171 bool include_borders = true;
172 for (TilingData::Iterator tile_iter(&tiling_, layer_rect, include_borders);
173 tile_iter;
174 ++tile_iter) {
175 PictureMap::iterator map_iter = picture_map_.find(tile_iter.index());
176 if (map_iter == picture_map_.end())
177 continue;
178 PictureInfo& info = map_iter->second;
179 Picture* picture = info.GetPicture();
180 if (!picture)
181 continue;
182
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);
195
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);
201 continue;
202 }
203
204 Region& region = it->second;
205 region.Subtract(content_clip);
206 }
207 }
208
RasterCommon(SkCanvas * canvas,SkDrawPictureCallback * callback,gfx::Rect canvas_rect,float contents_scale,RenderingStatsInstrumentation * rendering_stats_instrumentation,bool is_analysis)209 void PicturePileImpl::RasterCommon(
210 SkCanvas* canvas,
211 SkDrawPictureCallback* callback,
212 gfx::Rect canvas_rect,
213 float contents_scale,
214 RenderingStatsInstrumentation* rendering_stats_instrumentation,
215 bool is_analysis) {
216 DCHECK(contents_scale >= min_contents_scale_);
217
218 canvas->translate(-canvas_rect.x(), -canvas_rect.y());
219 gfx::SizeF total_content_size = gfx::ScaleSize(tiling_.total_size(),
220 contents_scale);
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);
224
225 canvas->clipRect(gfx::RectToSkRect(content_rect),
226 SkRegion::kIntersect_Op);
227
228 PictureRegionMap picture_region_map;
229 CoalesceRasters(
230 canvas_rect, content_rect, contents_scale, &picture_region_map);
231
232 #ifndef NDEBUG
233 Region total_clip;
234 #endif // NDEBUG
235
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();
240 ++it) {
241 Picture* picture = it->first;
242 Region negated_clip_region = it->second;
243
244 #ifndef NDEBUG
245 Region positive_clip = content_rect;
246 positive_clip.Subtract(negated_clip_region);
247 total_clip.Union(positive_clip);
248 #endif // NDEBUG
249
250 base::TimeDelta best_duration =
251 base::TimeDelta::FromInternalValue(std::numeric_limits<int64>::max());
252 int repeat_count = std::max(1, slow_down_raster_scale_factor_for_debug_);
253 int rasterized_pixel_count = 0;
254
255 for (int j = 0; j < repeat_count; ++j) {
256 base::TimeTicks start_time;
257 if (rendering_stats_instrumentation)
258 start_time = rendering_stats_instrumentation->StartRecording();
259
260 rasterized_pixel_count = picture->Raster(
261 canvas, callback, negated_clip_region, contents_scale);
262
263 if (rendering_stats_instrumentation) {
264 base::TimeDelta duration =
265 rendering_stats_instrumentation->EndRecording(start_time);
266 best_duration = std::min(best_duration, duration);
267 }
268 }
269
270 if (rendering_stats_instrumentation) {
271 if (is_analysis) {
272 rendering_stats_instrumentation->AddAnalysis(best_duration,
273 rasterized_pixel_count);
274 } else {
275 rendering_stats_instrumentation->AddRaster(best_duration,
276 rasterized_pixel_count);
277 }
278 }
279 }
280
281 #ifndef NDEBUG
282 // Fill the clip with debug color. This allows us to
283 // distinguish between non painted areas and problems with missing
284 // pictures.
285 SkPaint paint;
286 for (Region::Iterator it(total_clip); it.has_rect(); it.next())
287 canvas->clipRect(gfx::RectToSkRect(it.rect()), SkRegion::kDifference_Op);
288 paint.setColor(DebugColors::MissingPictureFillColor());
289 paint.setXfermodeMode(SkXfermode::kSrc_Mode);
290 canvas->drawPaint(paint);
291 #endif // NDEBUG
292 }
293
GetFlattenedPicture()294 skia::RefPtr<SkPicture> PicturePileImpl::GetFlattenedPicture() {
295 TRACE_EVENT0("cc", "PicturePileImpl::GetFlattenedPicture");
296
297 gfx::Rect layer_rect(tiling_.total_size());
298 skia::RefPtr<SkPicture> picture = skia::AdoptRef(new SkPicture);
299 if (layer_rect.IsEmpty())
300 return picture;
301
302 SkCanvas* canvas = picture->beginRecording(
303 layer_rect.width(),
304 layer_rect.height(),
305 SkPicture::kUsePathBoundsForClip_RecordingFlag);
306
307 RasterToBitmap(canvas, layer_rect, 1.0, NULL);
308 picture->endRecording();
309
310 return picture;
311 }
312
AnalyzeInRect(gfx::Rect content_rect,float contents_scale,PicturePileImpl::Analysis * analysis)313 void PicturePileImpl::AnalyzeInRect(
314 gfx::Rect content_rect,
315 float contents_scale,
316 PicturePileImpl::Analysis* analysis) {
317 AnalyzeInRect(content_rect, contents_scale, analysis, NULL);
318 }
319
AnalyzeInRect(gfx::Rect content_rect,float contents_scale,PicturePileImpl::Analysis * analysis,RenderingStatsInstrumentation * stats_instrumentation)320 void PicturePileImpl::AnalyzeInRect(
321 gfx::Rect content_rect,
322 float contents_scale,
323 PicturePileImpl::Analysis* analysis,
324 RenderingStatsInstrumentation* stats_instrumentation) {
325 DCHECK(analysis);
326 TRACE_EVENT0("cc", "PicturePileImpl::AnalyzeInRect");
327
328 gfx::Rect layer_rect = gfx::ScaleToEnclosingRect(
329 content_rect, 1.0f / contents_scale);
330
331 layer_rect.Intersect(gfx::Rect(tiling_.total_size()));
332
333 SkBitmap empty_bitmap;
334 empty_bitmap.setConfig(SkBitmap::kNo_Config,
335 layer_rect.width(),
336 layer_rect.height());
337 skia::AnalysisDevice device(empty_bitmap);
338 skia::AnalysisCanvas canvas(&device);
339
340 RasterForAnalysis(&canvas, layer_rect, 1.0f, stats_instrumentation);
341
342 analysis->is_solid_color = canvas.GetColorIfSolid(&analysis->solid_color);
343 analysis->has_text = canvas.HasText();
344 }
345
Analysis()346 PicturePileImpl::Analysis::Analysis()
347 : is_solid_color(false),
348 has_text(false) {
349 }
350
~Analysis()351 PicturePileImpl::Analysis::~Analysis() {
352 }
353
PixelRefIterator(gfx::Rect content_rect,float contents_scale,const PicturePileImpl * picture_pile)354 PicturePileImpl::PixelRefIterator::PixelRefIterator(
355 gfx::Rect content_rect,
356 float contents_scale,
357 const PicturePileImpl* picture_pile)
358 : picture_pile_(picture_pile),
359 layer_rect_(
360 gfx::ScaleToEnclosingRect(content_rect, 1.f / contents_scale)),
361 tile_iterator_(&picture_pile_->tiling_, layer_rect_, true) {
362 // TODO(enne): tile iterator should not include borders
363
364 // Early out if there isn't a single tile.
365 if (!tile_iterator_)
366 return;
367
368 AdvanceToTilePictureWithPixelRefs();
369 }
370
~PixelRefIterator()371 PicturePileImpl::PixelRefIterator::~PixelRefIterator() {
372 }
373
374 PicturePileImpl::PixelRefIterator&
operator ++()375 PicturePileImpl::PixelRefIterator::operator++() {
376 ++pixel_ref_iterator_;
377 if (pixel_ref_iterator_)
378 return *this;
379
380 ++tile_iterator_;
381 AdvanceToTilePictureWithPixelRefs();
382 return *this;
383 }
384
AdvanceToTilePictureWithPixelRefs()385 void PicturePileImpl::PixelRefIterator::AdvanceToTilePictureWithPixelRefs() {
386 for (; tile_iterator_; ++tile_iterator_) {
387 PictureMap::const_iterator it =
388 picture_pile_->picture_map_.find(tile_iterator_.index());
389 if (it == picture_pile_->picture_map_.end())
390 continue;
391
392 const Picture* picture = it->second.GetPicture();
393 if (!picture || (processed_pictures_.count(picture) != 0) ||
394 !picture->WillPlayBackBitmaps())
395 continue;
396
397 processed_pictures_.insert(picture);
398 pixel_ref_iterator_ = Picture::PixelRefIterator(layer_rect_, picture);
399 if (pixel_ref_iterator_)
400 break;
401 }
402 }
403
DidBeginTracing()404 void PicturePileImpl::DidBeginTracing() {
405 gfx::Rect layer_rect(tiling_.total_size());
406 std::set<void*> processed_pictures;
407 for (PictureMap::iterator it = picture_map_.begin();
408 it != picture_map_.end();
409 ++it) {
410 Picture* picture = it->second.GetPicture();
411 if (picture && (processed_pictures.count(picture) == 0)) {
412 picture->EmitTraceSnapshot();
413 processed_pictures.insert(picture);
414 }
415 }
416 }
417
418 } // namespace cc
419