picture_pile_impl.cc revision 1e9bf3e0803691d0a228da41fc608347b6db4340
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 19namespace cc { 20 21PicturePileImpl::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 30PicturePileImpl::ClonesForDrawing::~ClonesForDrawing() { 31} 32 33scoped_refptr<PicturePileImpl> PicturePileImpl::Create() { 34 return make_scoped_refptr(new PicturePileImpl); 35} 36 37scoped_refptr<PicturePileImpl> PicturePileImpl::CreateFromOther( 38 const PicturePileBase* other) { 39 return make_scoped_refptr(new PicturePileImpl(other)); 40} 41 42scoped_refptr<PicturePileImpl> PicturePileImpl::CreateCloneForDrawing( 43 const PicturePileImpl* other, unsigned thread_index) { 44 return make_scoped_refptr(new PicturePileImpl(other, thread_index)); 45} 46 47PicturePileImpl::PicturePileImpl() 48 : clones_for_drawing_(ClonesForDrawing(this, 0)) { 49} 50 51PicturePileImpl::PicturePileImpl(const PicturePileBase* other) 52 : PicturePileBase(other), 53 clones_for_drawing_(ClonesForDrawing(this, num_raster_threads())) { 54} 55 56PicturePileImpl::PicturePileImpl( 57 const PicturePileImpl* other, unsigned thread_index) 58 : PicturePileBase(other, thread_index), 59 clones_for_drawing_(ClonesForDrawing(this, 0)) { 60} 61 62PicturePileImpl::~PicturePileImpl() { 63} 64 65PicturePileImpl* 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 71void 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} 82 83void PicturePileImpl::RasterForAnalysis( 84 skia::AnalysisCanvas* canvas, 85 gfx::Rect canvas_rect, 86 float contents_scale) { 87 RasterCommon(canvas, canvas, canvas_rect, contents_scale, NULL); 88} 89 90void PicturePileImpl::RasterToBitmap( 91 SkCanvas* canvas, 92 gfx::Rect canvas_rect, 93 float contents_scale, 94 RenderingStatsInstrumentation* rendering_stats_instrumentation) { 95#ifndef NDEBUG 96 // Any non-painted areas will be left in this color. 97 canvas->clear(DebugColors::NonPaintedFillColor()); 98#endif // NDEBUG 99 100 // If this picture has opaque contents, it is guaranteeing that it will 101 // draw an opaque rect the size of the layer. If it is not, then we must 102 // clear this canvas ourselves. 103 if (!contents_opaque_) { 104 // Clearing is about ~4x faster than drawing a rect even if the content 105 // isn't covering a majority of the canvas. 106 canvas->clear(SK_ColorTRANSPARENT); 107 } else { 108 // Even if it is opaque, on any rasterizations that touch the edge of the 109 // layer, we also need to raster the background color underneath the last 110 // texel (since the recording won't cover it) and outside the last texel 111 // (due to linear filtering when using this texture). 112 gfx::SizeF total_content_size = gfx::ScaleSize(tiling_.total_size(), 113 contents_scale); 114 gfx::Rect content_rect(gfx::ToCeiledSize(total_content_size)); 115 gfx::Rect deflated_content_rect = content_rect; 116 content_rect.Intersect(canvas_rect); 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 deflated_content_rect.Inset(0, 0, 1, 1); 122 deflated_content_rect.Intersect(canvas_rect); 123 if (!deflated_content_rect.Contains(canvas_rect)) { 124 // Drawing at most 2 x 2 x (canvas width + canvas height) texels is 2-3X 125 // faster than clearing, so special case this. 126 canvas->save(); 127 gfx::Rect inflated_content_rect = content_rect; 128 inflated_content_rect.Inset(0, 0, -1, -1); 129 canvas->clipRect(gfx::RectToSkRect(inflated_content_rect), 130 SkRegion::kReplace_Op); 131 canvas->clipRect(gfx::RectToSkRect(deflated_content_rect), 132 SkRegion::kDifference_Op); 133 canvas->drawColor(background_color_, SkXfermode::kSrc_Mode); 134 canvas->restore(); 135 } 136 } 137 138 RasterCommon(canvas, 139 NULL, 140 canvas_rect, 141 contents_scale, 142 rendering_stats_instrumentation); 143} 144 145void PicturePileImpl::CoalesceRasters(gfx::Rect canvas_rect, 146 gfx::Rect content_rect, 147 float contents_scale, 148 PictureRegionMap* results) { 149 DCHECK(results); 150 // Rasterize the collection of relevant picture piles. 151 gfx::Rect layer_rect = gfx::ScaleToEnclosingRect( 152 content_rect, 1.f / contents_scale); 153 154 // Coalesce rasters of the same picture into different rects: 155 // - Compute the clip of each of the pile chunks, 156 // - Subtract it from the canvas rect to get difference region 157 // - Later, use the difference region to subtract each of the comprising 158 // rects from the canvas. 159 // Note that in essence, we're trying to mimic clipRegion with intersect op 160 // that also respects the current canvas transform and clip. In order to use 161 // the canvas transform, we must stick to clipRect operations (clipRegion 162 // ignores the transform). Intersect then can be written as subtracting the 163 // negation of the region we're trying to intersect. Luckily, we know that all 164 // of the rects will have to fit into |content_rect|, so we can start with 165 // that and subtract chunk rects to get the region that we need to subtract 166 // from the canvas. Then, we can use clipRect with difference op to subtract 167 // each rect in the region. 168 for (TilingData::Iterator tile_iter(&tiling_, layer_rect); 169 tile_iter; ++tile_iter) { 170 PictureMap::iterator map_iter = picture_map_.find(tile_iter.index()); 171 if (map_iter == picture_map_.end()) 172 continue; 173 PictureInfo& info = map_iter->second; 174 if (!info.picture.get()) 175 continue; 176 177 // This is intentionally *enclosed* rect, so that the clip is aligned on 178 // integral post-scale content pixels and does not extend past the edges 179 // of the picture chunk's layer rect. The min_contents_scale enforces that 180 // enough buffer pixels have been added such that the enclosed rect 181 // encompasses all invalidated pixels at any larger scale level. 182 gfx::Rect chunk_rect = PaddedRect(tile_iter.index()); 183 gfx::Rect content_clip = 184 gfx::ScaleToEnclosedRect(chunk_rect, contents_scale); 185 DCHECK(!content_clip.IsEmpty()) << "Layer rect: " 186 << info.picture->LayerRect().ToString() 187 << "Contents scale: " << contents_scale; 188 content_clip.Intersect(canvas_rect); 189 190 PictureRegionMap::iterator it = results->find(info.picture.get()); 191 if (it == results->end()) { 192 Region& region = (*results)[info.picture.get()]; 193 region = content_rect; 194 region.Subtract(content_clip); 195 continue; 196 } 197 198 Region& region = it->second; 199 region.Subtract(content_clip); 200 } 201} 202 203void PicturePileImpl::RasterCommon( 204 SkCanvas* canvas, 205 SkDrawPictureCallback* callback, 206 gfx::Rect canvas_rect, 207 float contents_scale, 208 RenderingStatsInstrumentation* rendering_stats_instrumentation) { 209 DCHECK(contents_scale >= min_contents_scale_); 210 211 canvas->translate(-canvas_rect.x(), -canvas_rect.y()); 212 gfx::SizeF total_content_size = gfx::ScaleSize(tiling_.total_size(), 213 contents_scale); 214 gfx::Rect total_content_rect(gfx::ToCeiledSize(total_content_size)); 215 gfx::Rect content_rect = total_content_rect; 216 content_rect.Intersect(canvas_rect); 217 218 canvas->clipRect(gfx::RectToSkRect(content_rect), 219 SkRegion::kIntersect_Op); 220 221 PictureRegionMap picture_region_map; 222 CoalesceRasters( 223 canvas_rect, content_rect, contents_scale, &picture_region_map); 224 225#ifndef NDEBUG 226 Region total_clip; 227#endif // NDEBUG 228 229 // Iterate the coalesced map and use each picture's region 230 // to clip the canvas. 231 for (PictureRegionMap::iterator it = picture_region_map.begin(); 232 it != picture_region_map.end(); 233 ++it) { 234 Picture* picture = it->first; 235 Region negated_clip_region = it->second; 236 237#ifndef NDEBUG 238 Region positive_clip = content_rect; 239 positive_clip.Subtract(negated_clip_region); 240 total_clip.Union(positive_clip); 241#endif // NDEBUG 242 243 base::TimeDelta best_duration = 244 base::TimeDelta::FromInternalValue(std::numeric_limits<int64>::max()); 245 int repeat_count = std::max(1, slow_down_raster_scale_factor_for_debug_); 246 int rasterized_pixel_count = 0; 247 248 for (int j = 0; j < repeat_count; ++j) { 249 base::TimeTicks start_time; 250 if (rendering_stats_instrumentation) 251 start_time = rendering_stats_instrumentation->StartRecording(); 252 253 rasterized_pixel_count = picture->Raster( 254 canvas, callback, negated_clip_region, contents_scale); 255 256 if (rendering_stats_instrumentation) { 257 base::TimeDelta duration = 258 rendering_stats_instrumentation->EndRecording(start_time); 259 best_duration = std::min(best_duration, duration); 260 } 261 } 262 263 if (rendering_stats_instrumentation) { 264 rendering_stats_instrumentation->AddRaster(best_duration, 265 rasterized_pixel_count); 266 } 267 } 268 269#ifndef NDEBUG 270 // Fill the clip with debug color. This allows us to 271 // distinguish between non painted areas and problems with missing 272 // pictures. 273 SkPaint paint; 274 for (Region::Iterator it(total_clip); it.has_rect(); it.next()) 275 canvas->clipRect(gfx::RectToSkRect(it.rect()), SkRegion::kDifference_Op); 276 paint.setColor(DebugColors::MissingPictureFillColor()); 277 paint.setXfermodeMode(SkXfermode::kSrc_Mode); 278 canvas->drawPaint(paint); 279#endif // NDEBUG 280} 281 282skia::RefPtr<SkPicture> PicturePileImpl::GetFlattenedPicture() { 283 TRACE_EVENT0("cc", "PicturePileImpl::GetFlattenedPicture"); 284 285 gfx::Rect layer_rect(tiling_.total_size()); 286 skia::RefPtr<SkPicture> picture = skia::AdoptRef(new SkPicture); 287 if (layer_rect.IsEmpty()) 288 return picture; 289 290 SkCanvas* canvas = picture->beginRecording( 291 layer_rect.width(), 292 layer_rect.height(), 293 SkPicture::kUsePathBoundsForClip_RecordingFlag); 294 295 RasterToBitmap(canvas, layer_rect, 1.0, NULL); 296 picture->endRecording(); 297 298 return picture; 299} 300 301void PicturePileImpl::AnalyzeInRect(gfx::Rect content_rect, 302 float contents_scale, 303 PicturePileImpl::Analysis* analysis) { 304 DCHECK(analysis); 305 TRACE_EVENT0("cc", "PicturePileImpl::AnalyzeInRect"); 306 307 gfx::Rect layer_rect = gfx::ScaleToEnclosingRect( 308 content_rect, 1.0f / contents_scale); 309 310 layer_rect.Intersect(gfx::Rect(tiling_.total_size())); 311 312 SkBitmap empty_bitmap; 313 empty_bitmap.setConfig(SkBitmap::kNo_Config, 314 layer_rect.width(), 315 layer_rect.height()); 316 skia::AnalysisDevice device(empty_bitmap); 317 skia::AnalysisCanvas canvas(&device); 318 319 RasterForAnalysis(&canvas, layer_rect, 1.0f); 320 321 analysis->is_solid_color = canvas.GetColorIfSolid(&analysis->solid_color); 322 analysis->has_text = canvas.HasText(); 323} 324 325PicturePileImpl::Analysis::Analysis() 326 : is_solid_color(false), 327 has_text(false) { 328} 329 330PicturePileImpl::Analysis::~Analysis() { 331} 332 333PicturePileImpl::PixelRefIterator::PixelRefIterator( 334 gfx::Rect content_rect, 335 float contents_scale, 336 const PicturePileImpl* picture_pile) 337 : picture_pile_(picture_pile), 338 layer_rect_(gfx::ScaleToEnclosingRect( 339 content_rect, 1.f / contents_scale)), 340 tile_iterator_(&picture_pile_->tiling_, layer_rect_) { 341 // Early out if there isn't a single tile. 342 if (!tile_iterator_) 343 return; 344 345 AdvanceToTilePictureWithPixelRefs(); 346} 347 348PicturePileImpl::PixelRefIterator::~PixelRefIterator() { 349} 350 351PicturePileImpl::PixelRefIterator& 352 PicturePileImpl::PixelRefIterator::operator++() { 353 ++pixel_ref_iterator_; 354 if (pixel_ref_iterator_) 355 return *this; 356 357 ++tile_iterator_; 358 AdvanceToTilePictureWithPixelRefs(); 359 return *this; 360} 361 362void PicturePileImpl::PixelRefIterator::AdvanceToTilePictureWithPixelRefs() { 363 for (; tile_iterator_; ++tile_iterator_) { 364 PictureMap::const_iterator it = 365 picture_pile_->picture_map_.find(tile_iterator_.index()); 366 if (it == picture_pile_->picture_map_.end()) 367 continue; 368 369 const Picture* picture = it->second.picture.get(); 370 if (!picture || (processed_pictures_.count(picture) != 0)) 371 continue; 372 373 processed_pictures_.insert(picture); 374 pixel_ref_iterator_ = Picture::PixelRefIterator(layer_rect_, picture); 375 if (pixel_ref_iterator_) 376 break; 377 } 378} 379 380void PicturePileImpl::DidBeginTracing() { 381 gfx::Rect layer_rect(tiling_.total_size()); 382 std::set<void*> processed_pictures; 383 for (PictureMap::iterator it = picture_map_.begin(); 384 it != picture_map_.end(); 385 ++it) { 386 Picture* picture = it->second.picture.get(); 387 if (picture && (processed_pictures.count(picture) == 0)) { 388 picture->EmitTraceSnapshot(); 389 processed_pictures.insert(picture); 390 } 391 } 392} 393 394} // namespace cc 395