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 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 30 PicturePileImpl::ClonesForDrawing::~ClonesForDrawing() { 31 } 32 33 scoped_refptr<PicturePileImpl> PicturePileImpl::Create() { 34 return make_scoped_refptr(new PicturePileImpl); 35 } 36 37 scoped_refptr<PicturePileImpl> PicturePileImpl::CreateFromOther( 38 const PicturePileBase* other) { 39 return make_scoped_refptr(new PicturePileImpl(other)); 40 } 41 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 47 PicturePileImpl::PicturePileImpl() 48 : clones_for_drawing_(ClonesForDrawing(this, 0)) { 49 } 50 51 PicturePileImpl::PicturePileImpl(const PicturePileBase* other) 52 : PicturePileBase(other), 53 clones_for_drawing_(ClonesForDrawing(this, num_raster_threads())) { 54 } 55 56 PicturePileImpl::PicturePileImpl( 57 const PicturePileImpl* other, unsigned thread_index) 58 : PicturePileBase(other, thread_index), 59 clones_for_drawing_(ClonesForDrawing(this, 0)) { 60 } 61 62 PicturePileImpl::~PicturePileImpl() { 63 } 64 65 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 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 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 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 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 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 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 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 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 346 PicturePileImpl::Analysis::Analysis() 347 : is_solid_color(false), 348 has_text(false) { 349 } 350 351 PicturePileImpl::Analysis::~Analysis() { 352 } 353 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 371 PicturePileImpl::PixelRefIterator::~PixelRefIterator() { 372 } 373 374 PicturePileImpl::PixelRefIterator& 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 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 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