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 "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" 20 21 namespace cc { 22 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); 29 } 30 } 31 32 PicturePileImpl::ClonesForDrawing::~ClonesForDrawing() { 33 } 34 35 scoped_refptr<PicturePileImpl> PicturePileImpl::Create() { 36 return make_scoped_refptr(new PicturePileImpl); 37 } 38 39 scoped_refptr<PicturePileImpl> PicturePileImpl::CreateFromOther( 40 const PicturePileBase* other) { 41 return make_scoped_refptr(new PicturePileImpl(other)); 42 } 43 44 scoped_refptr<PicturePileImpl> PicturePileImpl::CreateCloneForDrawing( 45 const PicturePileImpl* other, unsigned thread_index) { 46 return make_scoped_refptr(new PicturePileImpl(other, thread_index)); 47 } 48 49 PicturePileImpl::PicturePileImpl() 50 : clones_for_drawing_(ClonesForDrawing(this, 0)) { 51 } 52 53 PicturePileImpl::PicturePileImpl(const PicturePileBase* other) 54 : PicturePileBase(other), 55 clones_for_drawing_(ClonesForDrawing( 56 this, RasterWorkerPool::GetNumRasterThreads())) { 57 } 58 59 PicturePileImpl::PicturePileImpl( 60 const PicturePileImpl* other, unsigned thread_index) 61 : PicturePileBase(other, thread_index), 62 clones_for_drawing_(ClonesForDrawing(this, 0)) { 63 } 64 65 PicturePileImpl::~PicturePileImpl() { 66 } 67 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(); 72 } 73 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); 85 } 86 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); 94 } 95 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()); 105 } 106 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)); 117 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(); 136 } 137 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(); 150 } 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); 156 } 157 158 RasterCommon(canvas, 159 NULL, 160 canvas_rect, 161 contents_scale, 162 rendering_stats_instrumentation, 163 false); 164 } 165 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); 174 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; 181 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; 207 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); 220 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(); 232 } 233 234 last_col_index = tile_iter.index_x(); 235 last_row_index = tile_iter.index_y(); 236 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); 241 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; 252 } 253 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; 258 } 259 } 260 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_); 269 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); 274 275 canvas->clipRect(gfx::RectToSkRect(content_tiling_rect), 276 SkRegion::kIntersect_Op); 277 278 PictureRegionMap picture_region_map; 279 CoalesceRasters( 280 canvas_rect, content_tiling_rect, contents_scale, &picture_region_map); 281 282 #ifndef NDEBUG 283 Region total_clip; 284 #endif // NDEBUG 285 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; 293 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 301 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; 305 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(); 310 311 rasterized_pixel_count = picture->Raster( 312 canvas, callback, negated_clip_region, contents_scale); 313 314 if (rendering_stats_instrumentation) { 315 base::TimeDelta duration = 316 rendering_stats_instrumentation->EndRecording(start_time); 317 best_duration = std::min(best_duration, duration); 318 } 319 } 320 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); 328 } 329 } 330 } 331 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 343 } 344 345 skia::RefPtr<SkPicture> PicturePileImpl::GetFlattenedPicture() { 346 TRACE_EVENT0("cc", "PicturePileImpl::GetFlattenedPicture"); 347 348 gfx::Rect tiling_rect(tiling_.tiling_rect()); 349 SkPictureRecorder recorder; 350 SkCanvas* canvas = 351 recorder.beginRecording(tiling_rect.width(), tiling_rect.height()); 352 if (!tiling_rect.IsEmpty()) 353 RasterToBitmap(canvas, tiling_rect, 1.0, NULL); 354 skia::RefPtr<SkPicture> picture = skia::AdoptRef(recorder.endRecording()); 355 356 return picture; 357 } 358 359 void PicturePileImpl::AnalyzeInRect( 360 const gfx::Rect& content_rect, 361 float contents_scale, 362 PicturePileImpl::Analysis* analysis) { 363 AnalyzeInRect(content_rect, contents_scale, analysis, NULL); 364 } 365 366 void PicturePileImpl::AnalyzeInRect( 367 const gfx::Rect& content_rect, 368 float contents_scale, 369 PicturePileImpl::Analysis* analysis, 370 RenderingStatsInstrumentation* stats_instrumentation) { 371 DCHECK(analysis); 372 TRACE_EVENT0("cc", "PicturePileImpl::AnalyzeInRect"); 373 374 gfx::Rect layer_rect = gfx::ScaleToEnclosingRect( 375 content_rect, 1.0f / contents_scale); 376 377 layer_rect.Intersect(tiling_.tiling_rect()); 378 379 skia::AnalysisCanvas canvas(layer_rect.width(), layer_rect.height()); 380 381 RasterForAnalysis(&canvas, layer_rect, 1.0f, stats_instrumentation); 382 383 analysis->is_solid_color = canvas.GetColorIfSolid(&analysis->solid_color); 384 } 385 386 // Since there are situations when we can skip analysis, the variables have to 387 // be set to their safest values. That is, we have to assume that the tile is 388 // not solid color. As well, we have to assume that the tile has text so we 389 // don't early out incorrectly. 390 PicturePileImpl::Analysis::Analysis() : is_solid_color(false) { 391 } 392 393 PicturePileImpl::Analysis::~Analysis() { 394 } 395 396 PicturePileImpl::PixelRefIterator::PixelRefIterator( 397 const gfx::Rect& content_rect, 398 float contents_scale, 399 const PicturePileImpl* picture_pile) 400 : picture_pile_(picture_pile), 401 layer_rect_( 402 gfx::ScaleToEnclosingRect(content_rect, 1.f / contents_scale)), 403 tile_iterator_(&picture_pile_->tiling_, 404 layer_rect_, 405 false /* include_borders */) { 406 // Early out if there isn't a single tile. 407 if (!tile_iterator_) 408 return; 409 410 AdvanceToTilePictureWithPixelRefs(); 411 } 412 413 PicturePileImpl::PixelRefIterator::~PixelRefIterator() { 414 } 415 416 PicturePileImpl::PixelRefIterator& 417 PicturePileImpl::PixelRefIterator::operator++() { 418 ++pixel_ref_iterator_; 419 if (pixel_ref_iterator_) 420 return *this; 421 422 ++tile_iterator_; 423 AdvanceToTilePictureWithPixelRefs(); 424 return *this; 425 } 426 427 void PicturePileImpl::PixelRefIterator::AdvanceToTilePictureWithPixelRefs() { 428 for (; tile_iterator_; ++tile_iterator_) { 429 PictureMap::const_iterator it = 430 picture_pile_->picture_map_.find(tile_iterator_.index()); 431 if (it == picture_pile_->picture_map_.end()) 432 continue; 433 434 const Picture* picture = it->second.GetPicture(); 435 if (!picture || (processed_pictures_.count(picture) != 0) || 436 !picture->WillPlayBackBitmaps()) 437 continue; 438 439 processed_pictures_.insert(picture); 440 pixel_ref_iterator_ = Picture::PixelRefIterator(layer_rect_, picture); 441 if (pixel_ref_iterator_) 442 break; 443 } 444 } 445 446 void PicturePileImpl::DidBeginTracing() { 447 std::set<void*> processed_pictures; 448 for (PictureMap::iterator it = picture_map_.begin(); 449 it != picture_map_.end(); 450 ++it) { 451 Picture* picture = it->second.GetPicture(); 452 if (picture && (processed_pictures.count(picture) == 0)) { 453 picture->EmitTraceSnapshot(); 454 processed_pictures.insert(picture); 455 } 456 } 457 } 458 459 } // namespace cc 460