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 show_debug_picture_borders_(false), 47 clear_canvas_with_debug_color_(kDefaultClearCanvasSetting), 48 num_raster_threads_(0), 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 show_debug_picture_borders_(other->show_debug_picture_borders_), 67 clear_canvas_with_debug_color_(other->clear_canvas_with_debug_color_), 68 num_raster_threads_(other->num_raster_threads_), 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 show_debug_picture_borders_(other->show_debug_picture_borders_), 82 clear_canvas_with_debug_color_(other->clear_canvas_with_debug_color_), 83 num_raster_threads_(other->num_raster_threads_), 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::Resize(gfx::Size new_size) { 96 if (size() == new_size) 97 return; 98 99 gfx::Size old_size = size(); 100 tiling_.SetTotalSize(new_size); 101 102 has_any_recordings_ = false; 103 104 // Find all tiles that contain any pixels outside the new size. 105 std::vector<PictureMapKey> to_erase; 106 int min_toss_x = tiling_.FirstBorderTileXIndexFromSrcCoord( 107 std::min(old_size.width(), new_size.width())); 108 int min_toss_y = tiling_.FirstBorderTileYIndexFromSrcCoord( 109 std::min(old_size.height(), new_size.height())); 110 for (PictureMap::const_iterator it = picture_map_.begin(); 111 it != picture_map_.end(); 112 ++it) { 113 const PictureMapKey& key = it->first; 114 if (key.first < min_toss_x && key.second < min_toss_y) { 115 has_any_recordings_ |= !!it->second.GetPicture(); 116 continue; 117 } 118 to_erase.push_back(key); 119 } 120 121 for (size_t i = 0; i < to_erase.size(); ++i) 122 picture_map_.erase(to_erase[i]); 123 124 // Don't waste time in Resize figuring out what these hints should be. 125 recorded_viewport_ = gfx::Rect(); 126 } 127 128 void PicturePileBase::SetMinContentsScale(float min_contents_scale) { 129 DCHECK(min_contents_scale); 130 if (min_contents_scale_ == min_contents_scale) 131 return; 132 133 // Picture contents are played back scaled. When the final contents scale is 134 // less than 1 (i.e. low res), then multiple recorded pixels will be used 135 // to raster one final pixel. To avoid splitting a final pixel across 136 // pictures (which would result in incorrect rasterization due to blending), a 137 // buffer margin is added so that any picture can be snapped to integral 138 // final pixels. 139 // 140 // For example, if a 1/4 contents scale is used, then that would be 3 buffer 141 // pixels, since that's the minimum number of pixels to add so that resulting 142 // content can be snapped to a four pixel aligned grid. 143 int buffer_pixels = static_cast<int>(ceil(1 / min_contents_scale) - 1); 144 buffer_pixels = std::max(0, buffer_pixels); 145 SetBufferPixels(buffer_pixels); 146 min_contents_scale_ = min_contents_scale; 147 } 148 149 // static 150 void PicturePileBase::ComputeTileGridInfo( 151 gfx::Size tile_grid_size, 152 SkTileGridPicture::TileGridInfo* info) { 153 DCHECK(info); 154 info->fTileInterval.set(tile_grid_size.width() - 2 * kTileGridBorderPixels, 155 tile_grid_size.height() - 2 * kTileGridBorderPixels); 156 DCHECK_GT(info->fTileInterval.width(), 0); 157 DCHECK_GT(info->fTileInterval.height(), 0); 158 info->fMargin.set(kTileGridBorderPixels, kTileGridBorderPixels); 159 // Offset the tile grid coordinate space to take into account the fact 160 // that the top-most and left-most tiles do not have top and left borders 161 // respectively. 162 info->fOffset.set(-kTileGridBorderPixels, -kTileGridBorderPixels); 163 } 164 165 void PicturePileBase::SetTileGridSize(gfx::Size tile_grid_size) { 166 ComputeTileGridInfo(tile_grid_size, &tile_grid_info_); 167 } 168 169 void PicturePileBase::SetBufferPixels(int new_buffer_pixels) { 170 if (new_buffer_pixels == buffer_pixels()) 171 return; 172 173 Clear(); 174 tiling_.SetBorderTexels(new_buffer_pixels); 175 } 176 177 void PicturePileBase::Clear() { 178 picture_map_.clear(); 179 recorded_viewport_ = gfx::Rect(); 180 } 181 182 bool PicturePileBase::HasRecordingAt(int x, int y) { 183 PictureMap::const_iterator found = picture_map_.find(PictureMapKey(x, y)); 184 if (found == picture_map_.end()) 185 return false; 186 return !!found->second.GetPicture(); 187 } 188 189 bool PicturePileBase::CanRaster(float contents_scale, gfx::Rect content_rect) { 190 if (tiling_.total_size().IsEmpty()) 191 return false; 192 gfx::Rect layer_rect = gfx::ScaleToEnclosingRect( 193 content_rect, 1.f / contents_scale); 194 layer_rect.Intersect(gfx::Rect(tiling_.total_size())); 195 196 // Common case inside of viewport to avoid the slower map lookups. 197 if (recorded_viewport_.Contains(layer_rect)) { 198 // Sanity check that there are no false positives in recorded_viewport_. 199 DCHECK(CanRasterSlowTileCheck(layer_rect)); 200 return true; 201 } 202 203 return CanRasterSlowTileCheck(layer_rect); 204 } 205 206 bool PicturePileBase::CanRasterSlowTileCheck( 207 const gfx::Rect& layer_rect) const { 208 bool include_borders = false; 209 for (TilingData::Iterator tile_iter(&tiling_, layer_rect, include_borders); 210 tile_iter; 211 ++tile_iter) { 212 PictureMap::const_iterator map_iter = picture_map_.find(tile_iter.index()); 213 if (map_iter == picture_map_.end()) 214 return false; 215 if (!map_iter->second.GetPicture()) 216 return false; 217 } 218 return true; 219 } 220 221 gfx::Rect PicturePileBase::PaddedRect(const PictureMapKey& key) { 222 gfx::Rect tile = tiling_.TileBounds(key.first, key.second); 223 return PadRect(tile); 224 } 225 226 gfx::Rect PicturePileBase::PadRect(gfx::Rect rect) { 227 gfx::Rect padded_rect = rect; 228 padded_rect.Inset( 229 -buffer_pixels(), -buffer_pixels(), -buffer_pixels(), -buffer_pixels()); 230 return padded_rect; 231 } 232 233 scoped_ptr<base::Value> PicturePileBase::AsValue() const { 234 scoped_ptr<base::ListValue> pictures(new base::ListValue()); 235 gfx::Rect layer_rect(tiling_.total_size()); 236 std::set<void*> appended_pictures; 237 bool include_borders = true; 238 for (TilingData::Iterator tile_iter(&tiling_, layer_rect, include_borders); 239 tile_iter; 240 ++tile_iter) { 241 PictureMap::const_iterator map_iter = picture_map_.find(tile_iter.index()); 242 if (map_iter == picture_map_.end()) 243 continue; 244 245 Picture* picture = map_iter->second.GetPicture(); 246 if (picture && (appended_pictures.count(picture) == 0)) { 247 appended_pictures.insert(picture); 248 pictures->Append(TracedValue::CreateIDRef(picture).release()); 249 } 250 } 251 return pictures.PassAs<base::Value>(); 252 } 253 254 PicturePileBase::PictureInfo::PictureInfo() : last_frame_number_(0) {} 255 256 PicturePileBase::PictureInfo::~PictureInfo() {} 257 258 void PicturePileBase::PictureInfo::AdvanceInvalidationHistory( 259 int frame_number) { 260 DCHECK_GE(frame_number, last_frame_number_); 261 if (frame_number == last_frame_number_) 262 return; 263 264 invalidation_history_ <<= (frame_number - last_frame_number_); 265 last_frame_number_ = frame_number; 266 } 267 268 bool PicturePileBase::PictureInfo::Invalidate(int frame_number) { 269 AdvanceInvalidationHistory(frame_number); 270 invalidation_history_.set(0); 271 272 bool did_invalidate = !!picture_; 273 picture_ = NULL; 274 return did_invalidate; 275 } 276 277 bool PicturePileBase::PictureInfo::NeedsRecording(int frame_number, 278 int distance_to_visible) { 279 AdvanceInvalidationHistory(frame_number); 280 281 // We only need recording if we don't have a picture. Furthermore, we only 282 // need a recording if we're within frequent invalidation distance threshold 283 // or the invalidation is not frequent enough (below invalidation frequency 284 // threshold). 285 return !picture_ && 286 ((distance_to_visible <= kFrequentInvalidationDistanceThreshold) || 287 (GetInvalidationFrequency() < kInvalidationFrequencyThreshold)); 288 } 289 290 void PicturePileBase::PictureInfo::SetPicture(scoped_refptr<Picture> picture) { 291 picture_ = picture; 292 } 293 294 Picture* PicturePileBase::PictureInfo::GetPicture() const { 295 return picture_.get(); 296 } 297 298 PicturePileBase::PictureInfo PicturePileBase::PictureInfo::CloneForThread( 299 int thread_index) const { 300 PictureInfo info = *this; 301 if (picture_.get()) 302 info.picture_ = picture_->GetCloneForDrawingOnThread(thread_index); 303 return info; 304 } 305 306 float PicturePileBase::PictureInfo::GetInvalidationFrequency() const { 307 return invalidation_history_.count() / 308 static_cast<float>(INVALIDATION_FRAMES_TRACKED); 309 } 310 311 } // namespace cc 312