1 // Copyright 2013 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 "cc/resources/picture_pile_base.h" 6 7 #include <algorithm> 8 #include <set> 9 #include <vector> 10 11 #include "base/logging.h" 12 #include "base/values.h" 13 #include "cc/base/math_util.h" 14 #include "cc/debug/traced_value.h" 15 #include "third_party/skia/include/core/SkColor.h" 16 #include "ui/gfx/rect_conversions.h" 17 18 namespace { 19 // Dimensions of the tiles in this picture pile as well as the dimensions of 20 // the base picture in each tile. 21 const int kBasePictureSize = 512; 22 const int kTileGridBorderPixels = 1; 23 #ifdef NDEBUG 24 const bool kDefaultClearCanvasSetting = false; 25 #else 26 const bool kDefaultClearCanvasSetting = true; 27 #endif 28 29 // Invalidation frequency settings. kInvalidationFrequencyThreshold is a value 30 // between 0 and 1 meaning invalidation frequency between 0% and 100% that 31 // indicates when to stop invalidating offscreen regions. 32 // kFrequentInvalidationDistanceThreshold defines what it means to be 33 // "offscreen" in terms of distance to visible in css pixels. 34 const float kInvalidationFrequencyThreshold = 0.75f; 35 const int kFrequentInvalidationDistanceThreshold = 512; 36 37 } // namespace 38 39 namespace cc { 40 41 PicturePileBase::PicturePileBase() 42 : min_contents_scale_(0), 43 background_color_(SkColorSetARGBInline(0, 0, 0, 0)), 44 slow_down_raster_scale_factor_for_debug_(0), 45 contents_opaque_(false), 46 contents_fill_bounds_completely_(false), 47 show_debug_picture_borders_(false), 48 clear_canvas_with_debug_color_(kDefaultClearCanvasSetting), 49 has_any_recordings_(false) { 50 tiling_.SetMaxTextureSize(gfx::Size(kBasePictureSize, kBasePictureSize)); 51 tile_grid_info_.fTileInterval.setEmpty(); 52 tile_grid_info_.fMargin.setEmpty(); 53 tile_grid_info_.fOffset.setZero(); 54 } 55 56 PicturePileBase::PicturePileBase(const PicturePileBase* other) 57 : picture_map_(other->picture_map_), 58 tiling_(other->tiling_), 59 recorded_viewport_(other->recorded_viewport_), 60 min_contents_scale_(other->min_contents_scale_), 61 tile_grid_info_(other->tile_grid_info_), 62 background_color_(other->background_color_), 63 slow_down_raster_scale_factor_for_debug_( 64 other->slow_down_raster_scale_factor_for_debug_), 65 contents_opaque_(other->contents_opaque_), 66 contents_fill_bounds_completely_(other->contents_fill_bounds_completely_), 67 show_debug_picture_borders_(other->show_debug_picture_borders_), 68 clear_canvas_with_debug_color_(other->clear_canvas_with_debug_color_), 69 has_any_recordings_(other->has_any_recordings_) {} 70 71 PicturePileBase::PicturePileBase(const PicturePileBase* other, 72 unsigned thread_index) 73 : tiling_(other->tiling_), 74 recorded_viewport_(other->recorded_viewport_), 75 min_contents_scale_(other->min_contents_scale_), 76 tile_grid_info_(other->tile_grid_info_), 77 background_color_(other->background_color_), 78 slow_down_raster_scale_factor_for_debug_( 79 other->slow_down_raster_scale_factor_for_debug_), 80 contents_opaque_(other->contents_opaque_), 81 contents_fill_bounds_completely_(other->contents_fill_bounds_completely_), 82 show_debug_picture_borders_(other->show_debug_picture_borders_), 83 clear_canvas_with_debug_color_(other->clear_canvas_with_debug_color_), 84 has_any_recordings_(other->has_any_recordings_) { 85 for (PictureMap::const_iterator it = other->picture_map_.begin(); 86 it != other->picture_map_.end(); 87 ++it) { 88 picture_map_[it->first] = it->second.CloneForThread(thread_index); 89 } 90 } 91 92 PicturePileBase::~PicturePileBase() { 93 } 94 95 void PicturePileBase::SetTilingRect(const gfx::Rect& new_tiling_rect) { 96 if (tiling_rect() == new_tiling_rect) 97 return; 98 99 gfx::Rect old_tiling_rect = tiling_rect(); 100 tiling_.SetTilingRect(new_tiling_rect); 101 102 has_any_recordings_ = false; 103 104 // Don't waste time in Resize figuring out what these hints should be. 105 recorded_viewport_ = gfx::Rect(); 106 107 if (new_tiling_rect.origin() != old_tiling_rect.origin()) { 108 picture_map_.clear(); 109 return; 110 } 111 112 // Find all tiles that contain any pixels outside the new rect. 113 std::vector<PictureMapKey> to_erase; 114 int min_toss_x = tiling_.FirstBorderTileXIndexFromSrcCoord( 115 std::min(old_tiling_rect.right(), new_tiling_rect.right())); 116 int min_toss_y = tiling_.FirstBorderTileYIndexFromSrcCoord( 117 std::min(old_tiling_rect.bottom(), new_tiling_rect.bottom())); 118 for (PictureMap::const_iterator it = picture_map_.begin(); 119 it != picture_map_.end(); 120 ++it) { 121 const PictureMapKey& key = it->first; 122 if (key.first < min_toss_x && key.second < min_toss_y) { 123 has_any_recordings_ |= !!it->second.GetPicture(); 124 continue; 125 } 126 to_erase.push_back(key); 127 } 128 129 for (size_t i = 0; i < to_erase.size(); ++i) 130 picture_map_.erase(to_erase[i]); 131 } 132 133 void PicturePileBase::SetMinContentsScale(float min_contents_scale) { 134 DCHECK(min_contents_scale); 135 if (min_contents_scale_ == min_contents_scale) 136 return; 137 138 // Picture contents are played back scaled. When the final contents scale is 139 // less than 1 (i.e. low res), then multiple recorded pixels will be used 140 // to raster one final pixel. To avoid splitting a final pixel across 141 // pictures (which would result in incorrect rasterization due to blending), a 142 // buffer margin is added so that any picture can be snapped to integral 143 // final pixels. 144 // 145 // For example, if a 1/4 contents scale is used, then that would be 3 buffer 146 // pixels, since that's the minimum number of pixels to add so that resulting 147 // content can be snapped to a four pixel aligned grid. 148 int buffer_pixels = static_cast<int>(ceil(1 / min_contents_scale) - 1); 149 buffer_pixels = std::max(0, buffer_pixels); 150 SetBufferPixels(buffer_pixels); 151 min_contents_scale_ = min_contents_scale; 152 } 153 154 // static 155 void PicturePileBase::ComputeTileGridInfo( 156 const gfx::Size& tile_grid_size, 157 SkTileGridFactory::TileGridInfo* info) { 158 DCHECK(info); 159 info->fTileInterval.set(tile_grid_size.width() - 2 * kTileGridBorderPixels, 160 tile_grid_size.height() - 2 * kTileGridBorderPixels); 161 DCHECK_GT(info->fTileInterval.width(), 0); 162 DCHECK_GT(info->fTileInterval.height(), 0); 163 info->fMargin.set(kTileGridBorderPixels, kTileGridBorderPixels); 164 // Offset the tile grid coordinate space to take into account the fact 165 // that the top-most and left-most tiles do not have top and left borders 166 // respectively. 167 info->fOffset.set(-kTileGridBorderPixels, -kTileGridBorderPixels); 168 } 169 170 void PicturePileBase::SetTileGridSize(const gfx::Size& tile_grid_size) { 171 ComputeTileGridInfo(tile_grid_size, &tile_grid_info_); 172 } 173 174 void PicturePileBase::SetBufferPixels(int new_buffer_pixels) { 175 if (new_buffer_pixels == buffer_pixels()) 176 return; 177 178 Clear(); 179 tiling_.SetBorderTexels(new_buffer_pixels); 180 } 181 182 void PicturePileBase::Clear() { 183 picture_map_.clear(); 184 recorded_viewport_ = gfx::Rect(); 185 } 186 187 bool PicturePileBase::HasRecordingAt(int x, int y) { 188 PictureMap::const_iterator found = picture_map_.find(PictureMapKey(x, y)); 189 if (found == picture_map_.end()) 190 return false; 191 return !!found->second.GetPicture(); 192 } 193 194 bool PicturePileBase::CanRaster(float contents_scale, 195 const gfx::Rect& content_rect) { 196 if (tiling_.tiling_rect().IsEmpty()) 197 return false; 198 gfx::Rect layer_rect = gfx::ScaleToEnclosingRect( 199 content_rect, 1.f / contents_scale); 200 layer_rect.Intersect(tiling_.tiling_rect()); 201 202 // Common case inside of viewport to avoid the slower map lookups. 203 if (recorded_viewport_.Contains(layer_rect)) { 204 // Sanity check that there are no false positives in recorded_viewport_. 205 DCHECK(CanRasterSlowTileCheck(layer_rect)); 206 return true; 207 } 208 209 return CanRasterSlowTileCheck(layer_rect); 210 } 211 212 bool PicturePileBase::CanRasterSlowTileCheck( 213 const gfx::Rect& layer_rect) const { 214 bool include_borders = false; 215 for (TilingData::Iterator tile_iter(&tiling_, layer_rect, include_borders); 216 tile_iter; 217 ++tile_iter) { 218 PictureMap::const_iterator map_iter = picture_map_.find(tile_iter.index()); 219 if (map_iter == picture_map_.end()) 220 return false; 221 if (!map_iter->second.GetPicture()) 222 return false; 223 } 224 return true; 225 } 226 227 gfx::Rect PicturePileBase::PaddedRect(const PictureMapKey& key) { 228 gfx::Rect tile = tiling_.TileBounds(key.first, key.second); 229 return PadRect(tile); 230 } 231 232 gfx::Rect PicturePileBase::PadRect(const gfx::Rect& rect) { 233 gfx::Rect padded_rect = rect; 234 padded_rect.Inset( 235 -buffer_pixels(), -buffer_pixels(), -buffer_pixels(), -buffer_pixels()); 236 return padded_rect; 237 } 238 239 scoped_ptr<base::Value> PicturePileBase::AsValue() const { 240 scoped_ptr<base::ListValue> pictures(new base::ListValue()); 241 gfx::Rect tiling_rect(tiling_.tiling_rect()); 242 std::set<void*> appended_pictures; 243 bool include_borders = true; 244 for (TilingData::Iterator tile_iter(&tiling_, tiling_rect, include_borders); 245 tile_iter; 246 ++tile_iter) { 247 PictureMap::const_iterator map_iter = picture_map_.find(tile_iter.index()); 248 if (map_iter == picture_map_.end()) 249 continue; 250 251 Picture* picture = map_iter->second.GetPicture(); 252 if (picture && (appended_pictures.count(picture) == 0)) { 253 appended_pictures.insert(picture); 254 pictures->Append(TracedValue::CreateIDRef(picture).release()); 255 } 256 } 257 return pictures.PassAs<base::Value>(); 258 } 259 260 PicturePileBase::PictureInfo::PictureInfo() : last_frame_number_(0) {} 261 262 PicturePileBase::PictureInfo::~PictureInfo() {} 263 264 void PicturePileBase::PictureInfo::AdvanceInvalidationHistory( 265 int frame_number) { 266 DCHECK_GE(frame_number, last_frame_number_); 267 if (frame_number == last_frame_number_) 268 return; 269 270 invalidation_history_ <<= (frame_number - last_frame_number_); 271 last_frame_number_ = frame_number; 272 } 273 274 bool PicturePileBase::PictureInfo::Invalidate(int frame_number) { 275 AdvanceInvalidationHistory(frame_number); 276 invalidation_history_.set(0); 277 278 bool did_invalidate = !!picture_; 279 picture_ = NULL; 280 return did_invalidate; 281 } 282 283 bool PicturePileBase::PictureInfo::NeedsRecording(int frame_number, 284 int distance_to_visible) { 285 AdvanceInvalidationHistory(frame_number); 286 287 // We only need recording if we don't have a picture. Furthermore, we only 288 // need a recording if we're within frequent invalidation distance threshold 289 // or the invalidation is not frequent enough (below invalidation frequency 290 // threshold). 291 return !picture_ && 292 ((distance_to_visible <= kFrequentInvalidationDistanceThreshold) || 293 (GetInvalidationFrequency() < kInvalidationFrequencyThreshold)); 294 } 295 296 void PicturePileBase::PictureInfo::SetPicture(scoped_refptr<Picture> picture) { 297 picture_ = picture; 298 } 299 300 Picture* PicturePileBase::PictureInfo::GetPicture() const { 301 return picture_.get(); 302 } 303 304 PicturePileBase::PictureInfo PicturePileBase::PictureInfo::CloneForThread( 305 int thread_index) const { 306 PictureInfo info = *this; 307 if (picture_.get()) 308 info.picture_ = picture_->GetCloneForDrawingOnThread(thread_index); 309 return info; 310 } 311 312 float PicturePileBase::PictureInfo::GetInvalidationFrequency() const { 313 return invalidation_history_.count() / 314 static_cast<float>(INVALIDATION_FRAMES_TRACKED); 315 } 316 317 } // namespace cc 318