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/SkPictureRecorder.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" 19 20 namespace cc { 21 22 scoped_refptr<PicturePileImpl> PicturePileImpl::Create() { 23 return make_scoped_refptr(new PicturePileImpl); 24 } 25 26 scoped_refptr<PicturePileImpl> PicturePileImpl::CreateFromOther( 27 const PicturePileBase* other) { 28 return make_scoped_refptr(new PicturePileImpl(other)); 29 } 30 31 PicturePileImpl::PicturePileImpl() { 32 } 33 34 PicturePileImpl::PicturePileImpl(const PicturePileBase* other) 35 : PicturePileBase(other) { 36 } 37 38 PicturePileImpl::~PicturePileImpl() { 39 } 40 41 void PicturePileImpl::RasterDirect( 42 SkCanvas* canvas, 43 const gfx::Rect& canvas_rect, 44 float contents_scale, 45 RenderingStatsInstrumentation* rendering_stats_instrumentation) { 46 RasterCommon(canvas, 47 NULL, 48 canvas_rect, 49 contents_scale, 50 rendering_stats_instrumentation, 51 false); 52 } 53 54 void PicturePileImpl::RasterForAnalysis( 55 skia::AnalysisCanvas* canvas, 56 const gfx::Rect& canvas_rect, 57 float contents_scale, 58 RenderingStatsInstrumentation* stats_instrumentation) const { 59 RasterCommon( 60 canvas, canvas, canvas_rect, contents_scale, stats_instrumentation, true); 61 } 62 63 void PicturePileImpl::RasterToBitmap( 64 SkCanvas* canvas, 65 const gfx::Rect& canvas_rect, 66 float contents_scale, 67 RenderingStatsInstrumentation* rendering_stats_instrumentation) const { 68 canvas->discard(); 69 if (clear_canvas_with_debug_color_) { 70 // Any non-painted areas in the content bounds will be left in this color. 71 canvas->clear(DebugColors::NonPaintedFillColor()); 72 } 73 74 // If this picture has opaque contents, it is guaranteeing that it will 75 // draw an opaque rect the size of the layer. If it is not, then we must 76 // clear this canvas ourselves. 77 if (contents_opaque_ || contents_fill_bounds_completely_) { 78 // Even if completely covered, for rasterizations that touch the edge of the 79 // layer, we also need to raster the background color underneath the last 80 // texel (since the recording won't cover it) and outside the last texel 81 // (due to linear filtering when using this texture). 82 gfx::Rect content_tiling_rect = gfx::ToEnclosingRect( 83 gfx::ScaleRect(gfx::Rect(tiling_.tiling_size()), contents_scale)); 84 85 // The final texel of content may only be partially covered by a 86 // rasterization; this rect represents the content rect that is fully 87 // covered by content. 88 gfx::Rect deflated_content_tiling_rect = content_tiling_rect; 89 deflated_content_tiling_rect.Inset(0, 0, 1, 1); 90 if (!deflated_content_tiling_rect.Contains(canvas_rect)) { 91 if (clear_canvas_with_debug_color_) { 92 // Any non-painted areas outside of the content bounds are left in 93 // this color. If this is seen then it means that cc neglected to 94 // rerasterize a tile that used to intersect with the content rect 95 // after the content bounds grew. 96 canvas->save(); 97 canvas->translate(-canvas_rect.x(), -canvas_rect.y()); 98 canvas->clipRect(gfx::RectToSkRect(content_tiling_rect), 99 SkRegion::kDifference_Op); 100 canvas->drawColor(DebugColors::MissingResizeInvalidations(), 101 SkXfermode::kSrc_Mode); 102 canvas->restore(); 103 } 104 105 // Drawing at most 2 x 2 x (canvas width + canvas height) texels is 2-3X 106 // faster than clearing, so special case this. 107 canvas->save(); 108 canvas->translate(-canvas_rect.x(), -canvas_rect.y()); 109 gfx::Rect inflated_content_tiling_rect = content_tiling_rect; 110 inflated_content_tiling_rect.Inset(0, 0, -1, -1); 111 canvas->clipRect(gfx::RectToSkRect(inflated_content_tiling_rect), 112 SkRegion::kReplace_Op); 113 canvas->clipRect(gfx::RectToSkRect(deflated_content_tiling_rect), 114 SkRegion::kDifference_Op); 115 canvas->drawColor(background_color_, SkXfermode::kSrc_Mode); 116 canvas->restore(); 117 } 118 } else { 119 TRACE_EVENT_INSTANT0("cc", "SkCanvas::clear", TRACE_EVENT_SCOPE_THREAD); 120 // Clearing is about ~4x faster than drawing a rect even if the content 121 // isn't covering a majority of the canvas. 122 canvas->clear(SK_ColorTRANSPARENT); 123 } 124 125 RasterCommon(canvas, 126 NULL, 127 canvas_rect, 128 contents_scale, 129 rendering_stats_instrumentation, 130 false); 131 } 132 133 void PicturePileImpl::CoalesceRasters(const gfx::Rect& canvas_rect, 134 const gfx::Rect& content_rect, 135 float contents_scale, 136 PictureRegionMap* results) const { 137 DCHECK(results); 138 // Rasterize the collection of relevant picture piles. 139 gfx::Rect layer_rect = gfx::ScaleToEnclosingRect( 140 content_rect, 1.f / contents_scale); 141 142 // Make sure pictures don't overlap by keeping track of previous right/bottom. 143 int min_content_left = -1; 144 int min_content_top = -1; 145 int last_row_index = -1; 146 int last_col_index = -1; 147 gfx::Rect last_content_rect; 148 149 // Coalesce rasters of the same picture into different rects: 150 // - Compute the clip of each of the pile chunks, 151 // - Subtract it from the canvas rect to get difference region 152 // - Later, use the difference region to subtract each of the comprising 153 // rects from the canvas. 154 // Note that in essence, we're trying to mimic clipRegion with intersect op 155 // that also respects the current canvas transform and clip. In order to use 156 // the canvas transform, we must stick to clipRect operations (clipRegion 157 // ignores the transform). Intersect then can be written as subtracting the 158 // negation of the region we're trying to intersect. Luckily, we know that all 159 // of the rects will have to fit into |content_rect|, so we can start with 160 // that and subtract chunk rects to get the region that we need to subtract 161 // from the canvas. Then, we can use clipRect with difference op to subtract 162 // each rect in the region. 163 bool include_borders = true; 164 for (TilingData::Iterator tile_iter(&tiling_, layer_rect, include_borders); 165 tile_iter; 166 ++tile_iter) { 167 PictureMap::const_iterator map_iter = picture_map_.find(tile_iter.index()); 168 if (map_iter == picture_map_.end()) 169 continue; 170 const PictureInfo& info = map_iter->second; 171 const Picture* picture = info.GetPicture(); 172 if (!picture) 173 continue; 174 175 // This is intentionally *enclosed* rect, so that the clip is aligned on 176 // integral post-scale content pixels and does not extend past the edges 177 // of the picture chunk's layer rect. The min_contents_scale enforces that 178 // enough buffer pixels have been added such that the enclosed rect 179 // encompasses all invalidated pixels at any larger scale level. 180 gfx::Rect chunk_rect = PaddedRect(tile_iter.index()); 181 gfx::Rect content_clip = 182 gfx::ScaleToEnclosedRect(chunk_rect, contents_scale); 183 DCHECK(!content_clip.IsEmpty()) << "Layer rect: " 184 << picture->LayerRect().ToString() 185 << "Contents scale: " << contents_scale; 186 content_clip.Intersect(canvas_rect); 187 188 // Make sure iterator goes top->bottom. 189 DCHECK_GE(tile_iter.index_y(), last_row_index); 190 if (tile_iter.index_y() > last_row_index) { 191 // First tile in a new row. 192 min_content_left = content_clip.x(); 193 min_content_top = last_content_rect.bottom(); 194 } else { 195 // Make sure iterator goes left->right. 196 DCHECK_GT(tile_iter.index_x(), last_col_index); 197 min_content_left = last_content_rect.right(); 198 min_content_top = last_content_rect.y(); 199 } 200 201 last_col_index = tile_iter.index_x(); 202 last_row_index = tile_iter.index_y(); 203 204 // Only inset if the content_clip is less than then previous min. 205 int inset_left = std::max(0, min_content_left - content_clip.x()); 206 int inset_top = std::max(0, min_content_top - content_clip.y()); 207 content_clip.Inset(inset_left, inset_top, 0, 0); 208 209 PictureRegionMap::iterator it = results->find(picture); 210 Region* clip_region; 211 if (it == results->end()) { 212 // The clip for a set of coalesced pictures starts out clipping the entire 213 // canvas. Each picture added to the set must subtract its own bounds 214 // from the clip region, poking a hole so that the picture is unclipped. 215 clip_region = &(*results)[picture]; 216 *clip_region = canvas_rect; 217 } else { 218 clip_region = &it->second; 219 } 220 221 DCHECK(clip_region->Contains(content_clip)) 222 << "Content clips should not overlap."; 223 clip_region->Subtract(content_clip); 224 last_content_rect = content_clip; 225 } 226 } 227 228 void PicturePileImpl::RasterCommon( 229 SkCanvas* canvas, 230 SkDrawPictureCallback* callback, 231 const gfx::Rect& canvas_rect, 232 float contents_scale, 233 RenderingStatsInstrumentation* rendering_stats_instrumentation, 234 bool is_analysis) const { 235 DCHECK(contents_scale >= min_contents_scale_); 236 237 canvas->translate(-canvas_rect.x(), -canvas_rect.y()); 238 gfx::Rect content_tiling_rect = gfx::ToEnclosingRect( 239 gfx::ScaleRect(gfx::Rect(tiling_.tiling_size()), contents_scale)); 240 content_tiling_rect.Intersect(canvas_rect); 241 242 canvas->clipRect(gfx::RectToSkRect(content_tiling_rect), 243 SkRegion::kIntersect_Op); 244 245 PictureRegionMap picture_region_map; 246 CoalesceRasters( 247 canvas_rect, content_tiling_rect, contents_scale, &picture_region_map); 248 249 #ifndef NDEBUG 250 Region total_clip; 251 #endif // NDEBUG 252 253 // Iterate the coalesced map and use each picture's region 254 // to clip the canvas. 255 for (PictureRegionMap::iterator it = picture_region_map.begin(); 256 it != picture_region_map.end(); 257 ++it) { 258 const Picture* picture = it->first; 259 Region negated_clip_region = it->second; 260 261 #ifndef NDEBUG 262 Region positive_clip = content_tiling_rect; 263 positive_clip.Subtract(negated_clip_region); 264 // Make sure we never rasterize the same region twice. 265 DCHECK(!total_clip.Intersects(positive_clip)); 266 total_clip.Union(positive_clip); 267 #endif // NDEBUG 268 269 base::TimeDelta best_duration = base::TimeDelta::Max(); 270 int repeat_count = std::max(1, slow_down_raster_scale_factor_for_debug_); 271 int rasterized_pixel_count = 0; 272 273 for (int j = 0; j < repeat_count; ++j) { 274 base::TimeTicks start_time; 275 if (rendering_stats_instrumentation) 276 start_time = rendering_stats_instrumentation->StartRecording(); 277 278 rasterized_pixel_count = picture->Raster( 279 canvas, callback, negated_clip_region, contents_scale); 280 281 if (rendering_stats_instrumentation) { 282 base::TimeDelta duration = 283 rendering_stats_instrumentation->EndRecording(start_time); 284 best_duration = std::min(best_duration, duration); 285 } 286 } 287 288 if (rendering_stats_instrumentation) { 289 if (is_analysis) { 290 rendering_stats_instrumentation->AddAnalysis(best_duration, 291 rasterized_pixel_count); 292 } else { 293 rendering_stats_instrumentation->AddRaster(best_duration, 294 rasterized_pixel_count); 295 } 296 } 297 } 298 299 #ifndef NDEBUG 300 // Fill the clip with debug color. This allows us to 301 // distinguish between non painted areas and problems with missing 302 // pictures. 303 SkPaint paint; 304 for (Region::Iterator it(total_clip); it.has_rect(); it.next()) 305 canvas->clipRect(gfx::RectToSkRect(it.rect()), SkRegion::kDifference_Op); 306 paint.setColor(DebugColors::MissingPictureFillColor()); 307 paint.setXfermodeMode(SkXfermode::kSrc_Mode); 308 canvas->drawPaint(paint); 309 #endif // NDEBUG 310 } 311 312 skia::RefPtr<SkPicture> PicturePileImpl::GetFlattenedPicture() { 313 TRACE_EVENT0("cc", "PicturePileImpl::GetFlattenedPicture"); 314 315 gfx::Rect tiling_rect(tiling_.tiling_size()); 316 SkPictureRecorder recorder; 317 SkCanvas* canvas = 318 recorder.beginRecording(tiling_rect.width(), tiling_rect.height()); 319 if (!tiling_rect.IsEmpty()) 320 RasterToBitmap(canvas, tiling_rect, 1.0, NULL); 321 skia::RefPtr<SkPicture> picture = skia::AdoptRef(recorder.endRecording()); 322 323 return picture; 324 } 325 326 void PicturePileImpl::AnalyzeInRect(const gfx::Rect& content_rect, 327 float contents_scale, 328 PicturePileImpl::Analysis* analysis) const { 329 AnalyzeInRect(content_rect, contents_scale, analysis, NULL); 330 } 331 332 void PicturePileImpl::AnalyzeInRect( 333 const gfx::Rect& content_rect, 334 float contents_scale, 335 PicturePileImpl::Analysis* analysis, 336 RenderingStatsInstrumentation* stats_instrumentation) const { 337 DCHECK(analysis); 338 TRACE_EVENT0("cc", "PicturePileImpl::AnalyzeInRect"); 339 340 gfx::Rect layer_rect = gfx::ScaleToEnclosingRect( 341 content_rect, 1.0f / contents_scale); 342 343 layer_rect.Intersect(gfx::Rect(tiling_.tiling_size())); 344 345 skia::AnalysisCanvas canvas(layer_rect.width(), layer_rect.height()); 346 347 RasterForAnalysis(&canvas, layer_rect, 1.0f, stats_instrumentation); 348 349 analysis->is_solid_color = canvas.GetColorIfSolid(&analysis->solid_color); 350 } 351 352 // Since there are situations when we can skip analysis, the variables have to 353 // be set to their safest values. That is, we have to assume that the tile is 354 // not solid color. As well, we have to assume that the tile has text so we 355 // don't early out incorrectly. 356 PicturePileImpl::Analysis::Analysis() : is_solid_color(false) { 357 } 358 359 PicturePileImpl::Analysis::~Analysis() { 360 } 361 362 PicturePileImpl::PixelRefIterator::PixelRefIterator( 363 const gfx::Rect& content_rect, 364 float contents_scale, 365 const PicturePileImpl* picture_pile) 366 : picture_pile_(picture_pile), 367 layer_rect_( 368 gfx::ScaleToEnclosingRect(content_rect, 1.f / contents_scale)), 369 tile_iterator_(&picture_pile_->tiling_, 370 layer_rect_, 371 false /* include_borders */) { 372 // Early out if there isn't a single tile. 373 if (!tile_iterator_) 374 return; 375 376 AdvanceToTilePictureWithPixelRefs(); 377 } 378 379 PicturePileImpl::PixelRefIterator::~PixelRefIterator() { 380 } 381 382 PicturePileImpl::PixelRefIterator& 383 PicturePileImpl::PixelRefIterator::operator++() { 384 ++pixel_ref_iterator_; 385 if (pixel_ref_iterator_) 386 return *this; 387 388 ++tile_iterator_; 389 AdvanceToTilePictureWithPixelRefs(); 390 return *this; 391 } 392 393 void PicturePileImpl::PixelRefIterator::AdvanceToTilePictureWithPixelRefs() { 394 for (; tile_iterator_; ++tile_iterator_) { 395 PictureMap::const_iterator it = 396 picture_pile_->picture_map_.find(tile_iterator_.index()); 397 if (it == picture_pile_->picture_map_.end()) 398 continue; 399 400 const Picture* picture = it->second.GetPicture(); 401 if (!picture || (processed_pictures_.count(picture) != 0) || 402 !picture->WillPlayBackBitmaps()) 403 continue; 404 405 processed_pictures_.insert(picture); 406 pixel_ref_iterator_ = Picture::PixelRefIterator(layer_rect_, picture); 407 if (pixel_ref_iterator_) 408 break; 409 } 410 } 411 412 void PicturePileImpl::DidBeginTracing() { 413 std::set<const void*> processed_pictures; 414 for (PictureMap::iterator it = picture_map_.begin(); 415 it != picture_map_.end(); 416 ++it) { 417 const Picture* picture = it->second.GetPicture(); 418 if (picture && (processed_pictures.count(picture) == 0)) { 419 picture->EmitTraceSnapshot(); 420 processed_pictures.insert(picture); 421 } 422 } 423 } 424 425 } // namespace cc 426